summaryrefslogtreecommitdiff
path: root/src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs')
-rw-r--r--src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs395
1 files changed, 395 insertions, 0 deletions
diff --git a/src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs b/src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs
new file mode 100644
index 0000000000..744a48b107
--- /dev/null
+++ b/src/mscorlib/corefx/System/Globalization/CompareInfo.Windows.cs
@@ -0,0 +1,395 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ public partial class CompareInfo
+ {
+ internal unsafe CompareInfo(CultureInfo culture)
+ {
+ _name = culture._name;
+ InitSort(culture);
+ }
+
+ private void InitSort(CultureInfo culture)
+ {
+ _sortName = culture.SortName;
+
+ const uint LCMAP_SORTHANDLE = 0x20000000;
+
+ _name = culture._name;
+ _sortName = culture.SortName;
+
+ IntPtr handle;
+ int ret = Interop.mincore.LCMapStringEx(_sortName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero);
+ _sortHandle = ret > 0 ? handle : IntPtr.Zero;
+ }
+
+ private static unsafe int FindStringOrdinal(
+ uint dwFindStringOrdinalFlags,
+ string stringSource,
+ int offset,
+ int cchSource,
+ string value,
+ int cchValue,
+ bool bIgnoreCase)
+ {
+ fixed (char* pSource = stringSource)
+ fixed (char* pValue = value)
+ {
+ int ret = Interop.mincore.FindStringOrdinal(
+ dwFindStringOrdinalFlags,
+ pSource + offset,
+ cchSource,
+ pValue,
+ cchValue,
+ bIgnoreCase ? 1 : 0);
+ return ret < 0 ? ret : ret + offset;
+ }
+ }
+
+ internal static int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
+ {
+ Contract.Assert(source != null);
+ Contract.Assert(value != null);
+
+ return FindStringOrdinal(FIND_FROMSTART, source, startIndex, count, value, value.Length, ignoreCase);
+ }
+
+ internal static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
+ {
+ Contract.Assert(source != null);
+ Contract.Assert(value != null);
+
+ return FindStringOrdinal(FIND_FROMEND, source, startIndex - count + 1, count, value, value.Length, ignoreCase);
+ }
+
+ private unsafe int GetHashCodeOfStringCore(string source, CompareOptions options)
+ {
+ Contract.Assert(source != null);
+ Contract.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ if (source.Length == 0)
+ {
+ return 0;
+ }
+
+ int tmpHash = 0;
+
+ fixed (char* pSource = source)
+ {
+ if (Interop.mincore.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
+ LCMAP_HASH | (uint)GetNativeCompareFlags(options),
+ pSource, source.Length,
+ &tmpHash, sizeof(int),
+ null, null, _sortHandle) == 0)
+ {
+ Environment.FailFast("LCMapStringEx failed!");
+ }
+ }
+
+ return tmpHash;
+ }
+
+ private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2)
+ {
+ // Use the OS to compare and then convert the result to expected value by subtracting 2
+ return Interop.mincore.CompareStringOrdinal(string1, count1, string2, count2, true) - 2;
+ }
+
+ private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options)
+ {
+ Contract.Assert(string1 != null);
+ Contract.Assert(string2 != null);
+ Contract.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ string localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
+
+ fixed (char* pLocaleName = localeName)
+ fixed (char* pString1 = string1)
+ fixed (char* pString2 = string2)
+ {
+ int result = Interop.mincore.CompareStringEx(
+ pLocaleName,
+ (uint)GetNativeCompareFlags(options),
+ pString1 + offset1,
+ length1,
+ pString2 + offset2,
+ length2,
+ null,
+ null,
+ _sortHandle);
+
+ if (result == 0)
+ {
+ Environment.FailFast("CompareStringEx failed");
+ }
+
+ // Map CompareStringEx return value to -1, 0, 1.
+ return result - 2;
+ }
+ }
+
+ private unsafe int FindString(
+ uint dwFindNLSStringFlags,
+ string lpStringSource,
+ int startSource,
+ int cchSource,
+ string lpStringValue,
+ int startValue,
+ int cchValue)
+ {
+ string localeName = _sortHandle != IntPtr.Zero ? null : _sortName;
+
+ fixed (char* pLocaleName = localeName)
+ fixed (char* pSource = lpStringSource)
+ fixed (char* pValue = lpStringValue)
+ {
+ char* pS = pSource + startSource;
+ char* pV = pValue + startValue;
+
+ return Interop.mincore.FindNLSStringEx(
+ pLocaleName,
+ dwFindNLSStringFlags,
+ pS,
+ cchSource,
+ pV,
+ cchValue,
+ null,
+ null,
+ null,
+ _sortHandle);
+ }
+ }
+
+ private int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
+ {
+ Contract.Assert(!string.IsNullOrEmpty(source));
+ Contract.Assert(target != null);
+ Contract.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0);
+
+ // TODO: Consider moving this up to the relevent APIs we need to ensure this behavior for
+ // and add a precondition that target is not empty.
+ if (target.Length == 0)
+ return startIndex; // keep Whidbey compatibility
+
+ if ((options & CompareOptions.Ordinal) != 0)
+ {
+ return FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: false);
+ }
+ else
+ {
+ int retValue = FindString(FIND_FROMSTART | (uint)GetNativeCompareFlags(options),
+ source,
+ startIndex,
+ count,
+ target,
+ 0,
+ target.Length);
+ if (retValue >= 0)
+ {
+ return retValue + startIndex;
+ }
+ }
+
+ return -1;
+ }
+
+ private int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options)
+ {
+ Contract.Assert(!string.IsNullOrEmpty(source));
+ Contract.Assert(target != null);
+ Contract.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0);
+
+ // TODO: Consider moving this up to the relevent APIs we need to ensure this behavior for
+ // and add a precondition that target is not empty.
+ if (target.Length == 0)
+ return startIndex; // keep Whidbey compatibility
+
+ if ((options & CompareOptions.Ordinal) != 0)
+ {
+ return FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: true);
+ }
+ else
+ {
+ int retValue = FindString(FIND_FROMEND | (uint)GetNativeCompareFlags(options),
+ source,
+ startIndex - count + 1,
+ count,
+ target,
+ 0,
+ target.Length);
+
+ if (retValue >= 0)
+ {
+ return retValue + startIndex - (count - 1);
+ }
+ }
+
+ return -1;
+ }
+
+ private bool StartsWith(string source, string prefix, CompareOptions options)
+ {
+ Contract.Assert(!string.IsNullOrEmpty(source));
+ Contract.Assert(!string.IsNullOrEmpty(prefix));
+ Contract.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return FindString(FIND_STARTSWITH | (uint)GetNativeCompareFlags(options),
+ source,
+ 0,
+ source.Length,
+ prefix,
+ 0,
+ prefix.Length) >= 0;
+ }
+
+ private bool EndsWith(string source, string suffix, CompareOptions options)
+ {
+ Contract.Assert(!string.IsNullOrEmpty(source));
+ Contract.Assert(!string.IsNullOrEmpty(suffix));
+ Contract.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
+
+ return FindString(FIND_ENDSWITH | (uint)GetNativeCompareFlags(options),
+ source,
+ 0,
+ source.Length,
+ suffix,
+ 0,
+ suffix.Length) >= 0;
+ }
+
+ // PAL ends here
+ [NonSerialized]
+ private readonly IntPtr _sortHandle;
+
+ private const uint LCMAP_HASH = 0x00040000;
+
+ private const int FIND_STARTSWITH = 0x00100000;
+ private const int FIND_ENDSWITH = 0x00200000;
+ private const int FIND_FROMSTART = 0x00400000;
+ private const int FIND_FROMEND = 0x00800000;
+
+ // TODO: Instead of this method could we just have upstack code call IndexOfOrdinal with ignoreCase = false?
+ private static unsafe int FastIndexOfString(string source, string target, int startIndex, int sourceCount, int targetCount, bool findLastIndex)
+ {
+ int retValue = -1;
+
+ int sourceStartIndex = findLastIndex ? startIndex - sourceCount + 1 : startIndex;
+
+#if !TEST_CODEGEN_OPTIMIZATION
+ fixed (char* pSource = source, spTarget = target)
+ {
+ char* spSubSource = pSource + sourceStartIndex;
+#else
+ String.StringPointer spSubSource = source.GetStringPointer(sourceStartIndex);
+ String.StringPointer spTarget = target.GetStringPointer();
+#endif
+ if (findLastIndex)
+ {
+ int startPattern = (sourceCount - 1) - targetCount + 1;
+ if (startPattern < 0)
+ return -1;
+
+ char patternChar0 = spTarget[0];
+ for (int ctrSrc = startPattern; ctrSrc >= 0; ctrSrc--)
+ {
+ if (spSubSource[ctrSrc] != patternChar0)
+ continue;
+
+ int ctrPat;
+ for (ctrPat = 1; ctrPat < targetCount; ctrPat++)
+ {
+ if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat])
+ break;
+ }
+ if (ctrPat == targetCount)
+ {
+ retValue = ctrSrc;
+ break;
+ }
+ }
+
+ if (retValue >= 0)
+ {
+ retValue += startIndex - sourceCount + 1;
+ }
+ }
+ else
+ {
+ int endPattern = (sourceCount - 1) - targetCount + 1;
+ if (endPattern < 0)
+ return -1;
+
+ char patternChar0 = spTarget[0];
+ for (int ctrSrc = 0; ctrSrc <= endPattern; ctrSrc++)
+ {
+ if (spSubSource[ctrSrc] != patternChar0)
+ continue;
+ int ctrPat;
+ for (ctrPat = 1; ctrPat < targetCount; ctrPat++)
+ {
+ if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat])
+ break;
+ }
+ if (ctrPat == targetCount)
+ {
+ retValue = ctrSrc;
+ break;
+ }
+ }
+
+ if (retValue >= 0)
+ {
+ retValue += startIndex;
+ }
+ }
+#if !TEST_CODEGEN_OPTIMIZATION
+ }
+
+ return retValue;
+#endif // TEST_CODEGEN_OPTIMIZATION
+ }
+
+ private const int COMPARE_OPTIONS_ORDINAL = 0x40000000; // Ordinal
+ private const int NORM_IGNORECASE = 0x00000001; // Ignores case. (use LINGUISTIC_IGNORECASE instead)
+ private const int NORM_IGNOREKANATYPE = 0x00010000; // Does not differentiate between Hiragana and Katakana characters. Corresponding Hiragana and Katakana will compare as equal.
+ private const int NORM_IGNORENONSPACE = 0x00000002; // Ignores nonspacing. This flag also removes Japanese accent characters. (use LINGUISTIC_IGNOREDIACRITIC instead)
+ private const int NORM_IGNORESYMBOLS = 0x00000004; // Ignores symbols.
+ private const int NORM_IGNOREWIDTH = 0x00020000; // Does not differentiate between a single-byte character and the same character as a double-byte character.
+ private const int NORM_LINGUISTIC_CASING = 0x08000000; // use linguistic rules for casing
+ private const int SORT_STRINGSORT = 0x00001000; // Treats punctuation the same as symbols.
+
+ private static int GetNativeCompareFlags(CompareOptions options)
+ {
+ // Use "linguistic casing" by default (load the culture's casing exception tables)
+ int nativeCompareFlags = NORM_LINGUISTIC_CASING;
+
+ if ((options & CompareOptions.IgnoreCase) != 0) { nativeCompareFlags |= NORM_IGNORECASE; }
+ if ((options & CompareOptions.IgnoreKanaType) != 0) { nativeCompareFlags |= NORM_IGNOREKANATYPE; }
+ if ((options & CompareOptions.IgnoreNonSpace) != 0) { nativeCompareFlags |= NORM_IGNORENONSPACE; }
+ if ((options & CompareOptions.IgnoreSymbols) != 0) { nativeCompareFlags |= NORM_IGNORESYMBOLS; }
+ if ((options & CompareOptions.IgnoreWidth) != 0) { nativeCompareFlags |= NORM_IGNOREWIDTH; }
+ if ((options & CompareOptions.StringSort) != 0) { nativeCompareFlags |= SORT_STRINGSORT; }
+
+ // TODO: Can we try for GetNativeCompareFlags to never
+ // take Ordinal or OrdinalIgnoreCase. This value is not part of Win32, we just handle it special
+ // in some places.
+ // Suffix & Prefix shouldn't use this, make sure to turn off the NORM_LINGUISTIC_CASING flag
+ if (options == CompareOptions.Ordinal) { nativeCompareFlags = COMPARE_OPTIONS_ORDINAL; }
+
+ Contract.Assert(((options & ~(CompareOptions.IgnoreCase |
+ CompareOptions.IgnoreKanaType |
+ CompareOptions.IgnoreNonSpace |
+ CompareOptions.IgnoreSymbols |
+ CompareOptions.IgnoreWidth |
+ CompareOptions.StringSort)) == 0) ||
+ (options == CompareOptions.Ordinal), "[CompareInfo.GetNativeCompareFlags]Expected all flags to be handled");
+
+ return nativeCompareFlags;
+ }
+ }
+}