diff --git a/src/libraries/System.Private.Uri/src/System/UriHelper.cs b/src/libraries/System.Private.Uri/src/System/UriHelper.cs index 4a07e754a4d07..5d9665a67b7c0 100644 --- a/src/libraries/System.Private.Uri/src/System/UriHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/UriHelper.cs @@ -570,51 +570,45 @@ internal static bool IsLWS(char ch) return (ch <= ' ') && (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'); } - // // Is this a Bidirectional control char.. These get stripped - // - internal static bool IsBidiControlCharacter(char ch) - { - return (ch == '\u200E' /*LRM*/ || ch == '\u200F' /*RLM*/ || ch == '\u202A' /*LRE*/ || - ch == '\u202B' /*RLE*/ || ch == '\u202C' /*PDF*/ || ch == '\u202D' /*LRO*/ || - ch == '\u202E' /*RLO*/); - } + internal static bool IsBidiControlCharacter(char ch) => + char.IsBetween(ch, '\u200E', '\u202E') && !char.IsBetween(ch, '\u2010', '\u2029'); - // // Strip Bidirectional control characters from this string - // internal static unsafe string StripBidiControlCharacters(ReadOnlySpan strToClean, string? backingString = null) { Debug.Assert(backingString is null || strToClean.Length == backingString.Length); int charsToRemove = 0; - foreach (char c in strToClean) + + int indexOfPossibleCharToRemove = strToClean.IndexOfAnyInRange('\u200E', '\u202E'); + if (indexOfPossibleCharToRemove >= 0) { - if ((uint)(c - '\u200E') <= ('\u202E' - '\u200E') && IsBidiControlCharacter(c)) + // Slow path: Contains chars that fall in the [u200E, u202E] range (so likely Bidi) + foreach (char c in strToClean.Slice(indexOfPossibleCharToRemove)) { - charsToRemove++; + if (IsBidiControlCharacter(c)) + { + charsToRemove++; + } } } if (charsToRemove == 0) { + // Hot path return backingString ?? new string(strToClean); } - if (charsToRemove == strToClean.Length) - { - return string.Empty; - } - fixed (char* pStrToClean = &MemoryMarshal.GetReference(strToClean)) { - return string.Create(strToClean.Length - charsToRemove, (StrToClean: (IntPtr)pStrToClean, strToClean.Length), (buffer, state) => + return string.Create(strToClean.Length - charsToRemove, (StrToClean: (IntPtr)pStrToClean, strToClean.Length), static (buffer, state) => { var strToClean = new ReadOnlySpan((char*)state.StrToClean, state.Length); int destIndex = 0; foreach (char c in strToClean) { - if ((uint)(c - '\u200E') > ('\u202E' - '\u200E') || !IsBidiControlCharacter(c)) + if (!IsBidiControlCharacter(c)) { buffer[destIndex++] = c; }