Skip to content
Merged
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
102 changes: 40 additions & 62 deletions src/libraries/System.Private.Uri/src/System/Uri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,47 +641,36 @@ private static void GetCombinedString(Uri baseUri, string relativeStr,
// This parser will allow the relativeStr to be an absolute Uri with the different scheme
// In fact this is strict violation of RFC2396
//
for (int i = 0; i < relativeStr.Length; ++i)
{
if (relativeStr[i] == '/' || relativeStr[i] == '\\' || relativeStr[i] == '?' || relativeStr[i] == '#')
{
break;
}
else if (relativeStr[i] == ':')
{
if (i < 2)
{
// Note we don't support one-letter Uri schemes.
// Hence anything like x:sdsd is a relative path and be added to the baseUri Path
break;
}
int i = relativeStr.IndexOfAny(s_segmentSeparatorChars);

ParsingError error = ParsingError.None;
UriParser? syntax = CheckSchemeSyntax(relativeStr.AsSpan(0, i), ref error);
// Note we don't support one-letter Uri schemes (i > 1).
// Hence anything like x:sdsd is a relative path and be added to the baseUri Path
if ((uint)i < (uint)relativeStr.Length && relativeStr[i] == ':' && i > 1)
{
ParsingError error = ParsingError.None;
UriParser? syntax = CheckSchemeSyntax(relativeStr.AsSpan(0, i), ref error);

if (error == ParsingError.None)
if (error == ParsingError.None)
{
if (baseUri.Syntax == syntax)
{
if (baseUri.Syntax == syntax)
//Remove the scheme for backward Uri parsers compatibility
if (i + 1 < relativeStr.Length)
{
//Remove the scheme for backward Uri parsers compatibility
if (i + 1 < relativeStr.Length)
{
relativeStr = relativeStr.Substring(i + 1);
}
else
{
relativeStr = string.Empty;
}
relativeStr = relativeStr.Substring(i + 1);
}
else
{
// This is the place where we switch the scheme.
// Return relative part as the result Uri.
result = relativeStr;
return;
relativeStr = string.Empty;
}
}
break;
else
{
// This is the place where we switch the scheme.
// Return relative part as the result Uri.
result = relativeStr;
return;
}
}
}

Expand Down Expand Up @@ -3672,6 +3661,9 @@ private static int ParseSchemeCheckImplicitFile(string uriString, ref ParsingErr
return UriParser.FindOrFetchAsUnknownV1Syntax(UriHelper.SpanToLowerInvariantString(scheme));
}

private static readonly SearchValues<char> s_userInfoEndChars =
SearchValues.Create(@"@?#\/");

// Checks the syntax of an authority component. It may also get a userInfo if present
// Returns an error if no/mailformed authority found
// Does not NOT touch _info
Expand Down Expand Up @@ -3718,33 +3710,24 @@ private int CheckAuthorityHelper(ReadOnlySpan<char> str, int startOffset, out Pa

if ((syntaxFlags & UriSyntaxFlags.MayHaveUserInfo) != 0)
{
for (; (uint)i < (uint)str.Length; i++)
ReadOnlySpan<char> slice = str.Slice(i);
int userInfoLength = slice.IndexOfAny(s_userInfoEndChars);

// Check if the first delimiter is '@' and there's at least one character after it.
if ((uint)userInfoLength < (uint)slice.Length && slice[userInfoLength] == '@' && (uint)(++userInfoLength) < (uint)slice.Length)
{
ch = str[i];
ch = slice[userInfoLength];
i += userInfoLength;

if ((uint)(i + 1) >= (uint)str.Length || ch == '?' || ch == '#' || ch == '\\' || ch == '/')
{
ch = str[startOffset];
i = startOffset;
break;
}
flags |= Flags.HasUserInfo;

if (ch == '@')
// Iri'ze userinfo
if (hasUnicode)
{
flags |= Flags.HasUserInfo;

// Iri'ze userinfo
if (hasUnicode)
{
var vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]);
IriHelper.EscapeUnescapeIri(ref vsb, str.Slice(startOffset, i - startOffset + 1), isQuery: false);
newHost = string.Concat(newHost, vsb.AsSpan());
vsb.Dispose();
}

ch = str[i + 1];
i++;
break;
var vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]);
IriHelper.EscapeUnescapeIri(ref vsb, slice.Slice(0, userInfoLength), isQuery: false);
newHost = string.Concat(newHost, vsb.AsSpan());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could put newHost in vsb first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we'd just be copying the original newHost around an extra time in that case. We do this sort of thing in more places to avoid copying the prefix again.

In the long term my hope is that this substring allocation will get removed anyway and we'd only allocate the new _string once.

vsb.Dispose();
}
}
}
Expand Down Expand Up @@ -3925,13 +3908,8 @@ str[delimiterIdx] is not ('/' or '\\') &&
if ((syntaxFlags & UriSyntaxFlags.AllowAnyOtherHost) != 0)
{
flags |= Flags.BasicHostType;
for (endOfHost = i; (uint)endOfHost < (uint)str.Length; endOfHost++)
{
if (str[endOfHost] == '/' || str[endOfHost] == '?' || str[endOfHost] == '#')
{
break;
}
}
int basicHostEnd = str.Slice(i).IndexOfAny('/', '?', '#');
endOfHost = basicHostEnd >= 0 ? i + basicHostEnd : str.Length;

if (hasUnicode)
{
Expand Down