Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/libraries/System.Private.Uri/src/System/Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4015,11 +4015,9 @@ private unsafe int CheckAuthorityHelper(char* pString, int idx, int length,
if (hasUnicode)
{
// Normalize any other host or do idn
string user = new string(pString, startInput, end - startInput);

try
{
newHost += user.Normalize(NormalizationForm.FormC);
newHost = UriHelper.NormalizeAndConcat(newHost, new ReadOnlySpan<char>(pString + startInput, end - startInput));
}
catch (ArgumentException)
{
Expand Down Expand Up @@ -4061,10 +4059,9 @@ private unsafe int CheckAuthorityHelper(char* pString, int idx, int length,
if (hasUnicode)
{
// Normalize any other host
string user = new string(pString, startOtherHost, end - startOtherHost);
try
{
newHost += user.Normalize(NormalizationForm.FormC);
newHost = UriHelper.NormalizeAndConcat(newHost, new ReadOnlySpan<char>(pString + startOtherHost, end - startOtherHost));
}
catch (ArgumentException)
{
Expand Down Expand Up @@ -4095,10 +4092,16 @@ private static unsafe void CheckAuthorityHelperHandleDnsIri(char* pString, int s

if (hasUnicode)
{
string temp = UriHelper.StripBidiControlCharacters(new ReadOnlySpan<char>(pString + start, end - start));
ReadOnlySpan<char> host = new ReadOnlySpan<char>(pString + start, end - start);

if (UriHelper.StripBidiControlCharacters(host, out string? stripped))
{
host = stripped;
}

try
{
newHost += temp.Normalize(NormalizationForm.FormC);
newHost = UriHelper.NormalizeAndConcat(newHost, host);
}
catch (ArgumentException)
{
Expand Down
34 changes: 31 additions & 3 deletions src/libraries/System.Private.Uri/src/System/UriHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;

Expand All @@ -19,6 +20,21 @@ public static string SpanToLowerInvariantString(ReadOnlySpan<char> span)
});
}

public static string NormalizeAndConcat(string? start, ReadOnlySpan<char> toNormalize)
{
var vsb = new ValueStringBuilder(stackalloc char[Uri.StackallocThreshold]);
Comment thread
MihaZupan marked this conversation as resolved.

int charsWritten;
while (!toNormalize.TryNormalize(vsb.RawChars, out charsWritten, NormalizationForm.FormC))
{
vsb.EnsureCapacity(vsb.Capacity + 1);
}

string result = string.Concat(start, vsb.RawChars.Slice(0, charsWritten));
vsb.Dispose();
return result;
}

// http://host/Path/Path/File?Query is the base of
// - http://host/Path/Path/File/ ... (those "File" words may be different in semantic but anyway)
// - http://host/Path/Path/#Fragment
Expand Down Expand Up @@ -591,10 +607,20 @@ 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 string StripBidiControlCharacters(ReadOnlySpan<char> strToClean, string? backingString = null)
public static string StripBidiControlCharacters(ReadOnlySpan<char> strToClean, string? backingString = null)
{
Debug.Assert(backingString is null || strToClean.Length == backingString.Length);

if (StripBidiControlCharacters(strToClean, out string? stripped))
{
return stripped;
}

return backingString ?? strToClean.ToString();
}

public static bool StripBidiControlCharacters(ReadOnlySpan<char> strToClean, [NotNullWhen(true)] out string? stripped)
{
int charsToRemove = 0;

int indexOfPossibleCharToRemove = strToClean.IndexOfAnyInRange('\u200E', '\u202E');
Expand All @@ -613,10 +639,11 @@ internal static string StripBidiControlCharacters(ReadOnlySpan<char> strToClean,
if (charsToRemove == 0)
{
// Hot path
return backingString ?? new string(strToClean);
stripped = null;
return false;
}

return string.Create(strToClean.Length - charsToRemove, strToClean, static (buffer, strToClean) =>
stripped = string.Create(strToClean.Length - charsToRemove, strToClean, static (buffer, strToClean) =>
{
int destIndex = 0;
foreach (char c in strToClean)
Expand All @@ -628,6 +655,7 @@ internal static string StripBidiControlCharacters(ReadOnlySpan<char> strToClean,
}
Debug.Assert(buffer.Length == destIndex);
});
return true;
}
}
}