-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Remove early exit logic in GetSortKey-related code paths #1078
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -129,18 +129,24 @@ private unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOpt | |
| Debug.Assert(!GlobalizationMode.Invariant); | ||
| Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); | ||
|
|
||
| if (source.Length == 0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please check if Windows/ICU returns the same hash value for the empty string with all locales or cultures? I am asking because if this is the case, we can cache this value and keep the fast path for the empty strings.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, I am not actively following up so either don't block on me or wait till I am back. Thanks!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or at least maybe we could cache it for invariant culture (if not already)?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this but currently I don't have any evidence that anybody is trying to compute the culture-aware hash code of an empty string in a hot path. And yeah, no rush whatsoever on this. It can wait until the new year. :) |
||
| // LCMapStringEx doesn't support passing cchSrc = 0, so if given a null or empty input | ||
| // we'll normalize it to an empty null-terminated string and pass -1 to indicate that | ||
| // the underlying OS function should read until it encounters the null terminator. | ||
|
|
||
| int sourceLength = source.Length; | ||
| if (sourceLength == 0) | ||
| { | ||
| return 0; | ||
| source = string.Empty; | ||
| sourceLength = -1; | ||
| } | ||
|
|
||
| uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options); | ||
|
|
||
| fixed (char* pSource = source) | ||
| fixed (char* pSource = &MemoryMarshal.GetReference(source)) | ||
| { | ||
| int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, source.Length /* in chars */, | ||
| pSource, sourceLength /* in chars */, | ||
| null, 0, | ||
| null, null, _sortHandle); | ||
| if (sortKeyLength == 0) | ||
|
|
@@ -162,7 +168,7 @@ private unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOpt | |
| { | ||
| if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, source.Length /* in chars */, | ||
| pSource, sourceLength /* in chars */, | ||
| pSortKey, sortKeyLength, | ||
| null, null, _sortHandle) != sortKeyLength) | ||
| { | ||
|
|
@@ -505,38 +511,40 @@ private unsafe SortKey CreateSortKey(string source, CompareOptions options) | |
| } | ||
|
|
||
| byte[] keyData; | ||
| if (source.Length == 0) | ||
| uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options); | ||
|
|
||
| // LCMapStringEx doesn't support passing cchSrc = 0, so if given an empty string | ||
| // we'll instead pass -1 to indicate a null-terminated empty string. | ||
|
|
||
| int sourceLength = source.Length; | ||
| if (sourceLength == 0) | ||
| { | ||
| keyData = Array.Empty<byte>(); | ||
| sourceLength = -1; | ||
| } | ||
| else | ||
| { | ||
| uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options); | ||
|
|
||
| fixed (char* pSource = source) | ||
| fixed (char* pSource = source) | ||
| { | ||
| int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, sourceLength, | ||
| null, 0, | ||
| null, null, _sortHandle); | ||
| if (sortKeyLength == 0) | ||
| { | ||
| int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, source.Length, | ||
| null, 0, | ||
| null, null, _sortHandle); | ||
| if (sortKeyLength == 0) | ||
| { | ||
| throw new ArgumentException(SR.Arg_ExternalException); | ||
| } | ||
| throw new ArgumentException(SR.Arg_ExternalException); | ||
| } | ||
|
|
||
| keyData = new byte[sortKeyLength]; | ||
| keyData = new byte[sortKeyLength]; | ||
|
|
||
| fixed (byte* pBytes = keyData) | ||
| fixed (byte* pBytes = keyData) | ||
| { | ||
| if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, sourceLength, | ||
| pBytes, keyData.Length, | ||
| null, null, _sortHandle) != sortKeyLength) | ||
| { | ||
| if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, | ||
| flags, | ||
| pSource, source.Length, | ||
| pBytes, keyData.Length, | ||
| null, null, _sortHandle) != sortKeyLength) | ||
| { | ||
| throw new ArgumentException(SR.Arg_ExternalException); | ||
| } | ||
| throw new ArgumentException(SR.Arg_ExternalException); | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the expected result here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that on NLS (Windows) they're equal and that on ICU (non-Windows) they're unequal, but I'm not current on a box where I can easily check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked this today - my statement above is correct. Under the default / invariant culture, on ICU
"\ufffd"and"\u200c"are not equal, but using Windows'sCompareStringExthey are equal.