Skip to content
Open
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
51 changes: 22 additions & 29 deletions src/libraries/System.Private.Uri/src/System/IriHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,62 +69,55 @@ internal static bool CheckIriUnicodeRange(uint value, bool isQuery)
public static bool IsInInclusiveRange(uint value, uint min, uint max)
=> (value - min) <= (max - min);

//
// IRI normalization for strings containing characters that are not allowed or
// escaped characters that should be unescaped in the context of the specified Uri component.
//
internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end, bool isQuery)
internal static string EscapeUnescapeIri(ReadOnlySpan<char> span, bool isQuery)
{
Debug.Assert(end >= 0 && start >= 0 && start <= end);

int size = end - start;
var dest = size <= Uri.StackallocThreshold
var dest = span.Length <= Uri.StackallocThreshold
? new ValueStringBuilder(stackalloc char[Uri.StackallocThreshold])
: new ValueStringBuilder(size);
: new ValueStringBuilder(span.Length);

Span<byte> maxUtf8EncodedSpan = stackalloc byte[4];

for (int i = start; i < end; ++i)
for (int i = 0; (uint)i < (uint)span.Length; i++)
{
char ch = pInput[i];
char ch = span[i];

if (ch == '%')
{
if (end - i > 2)
if ((uint)(i + 2) < (uint)span.Length)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if ((uint)(i + 2) < (uint)span.Length)
if ((uint)i < (uint)(span.Length - 2))

To avoid re-computing i + 2 on every iteration. But does this also elide the bound check?

Copy link
Member Author

@MihaZupan MihaZupan Nov 16, 2025

Choose a reason for hiding this comment

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

But does this also elide the bound check?

It does not with the proposed change.

Copy link
Member

Choose a reason for hiding this comment

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

@EgorBo can the JIT learn to elide the bound checks here (or in M2 in this demo)?

{
ch = UriHelper.DecodeHexChars(pInput[i + 1], pInput[i + 2]);
ch = UriHelper.DecodeHexChars(span[i + 1], span[i + 2]);

// Do not unescape a reserved char
if (ch == Uri.c_DummyChar || UriHelper.IsNotSafeForUnescape(ch))
{
// keep as is
dest.Append(pInput[i++]);
dest.Append(pInput[i++]);
dest.Append(pInput[i]);
continue;
dest.Append(span[i]);
dest.Append(span[i + 1]);
dest.Append(span[i + 2]);
i += 2;
}
else if (ch <= '\x7F')
{
// ASCII
dest.Append(ch);
i += 2;
continue;
}
else
{
// possibly utf8 encoded sequence of unicode
int charactersRead = PercentEncodingHelper.UnescapePercentEncodedUTF8Sequence(
new ReadOnlySpan<char>(pInput + i, end - i),
span.Slice(i),
ref dest,
isQuery,
iriParsing: true);

Debug.Assert(charactersRead > 0);
i += charactersRead - 1; // -1 as i will be incremented in the loop
}
}
else
{
dest.Append(pInput[i]);

continue;
}
}
else if (ch > '\x7f')
Expand All @@ -136,9 +129,9 @@ internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end

char ch2 = '\0';

if ((char.IsHighSurrogate(ch)) && (i + 1 < end))
if ((char.IsHighSurrogate(ch)) && (uint)(i + 1) < (uint)span.Length)
{
ch2 = pInput[i + 1];
ch2 = span[i + 1];
isInIriUnicodeRange = CheckIriUnicodeRange(ch, ch2, out surrogatePair, isQuery);
}
else
Expand Down Expand Up @@ -179,12 +172,12 @@ internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end
{
i++;
}

continue;
}
else
{
// ASCII, just copy the character
dest.Append(pInput[i]);
}

// ASCII, just copy the character
dest.Append(ch);
}

return dest.ToString();
Expand Down
55 changes: 24 additions & 31 deletions src/libraries/System.Private.Uri/src/System/UncNameHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,27 @@ public static string ParseCanonicalName(string str, int start, int end, ref bool
//
// Assumption is the caller will check on the resulting name length
// Remarks: MUST NOT be used unless all input indexes are verified and trusted.
public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bool notImplicitFile)
public static bool IsValid(ReadOnlySpan<char> name, bool notImplicitFile, out int nameLength)
{
int end = returnedEnd;
nameLength = 0;

if (start == end)
return false;
//
// First segment could consist of only '_' or '-' but it cannot be all digits or empty
//
bool validShortName = false;
int i = start;
for (; i < end; ++i)
int i = 0;
for (; i < name.Length; i++)
{
if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
if (char.IsLetter(name[i]) || name[i] == '-' || name[i] == '_')
{
end = i;
break;
validShortName = true;
}
else if (name[i] == '.')
else if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
{
++i;
break;
}

if (char.IsLetter(name[i]) || name[i] == '-' || name[i] == '_')
else if (name[i] == '.')
{
validShortName = true;
i++;
break;
}
else if (!char.IsAsciiDigit(name[i]))
{
Expand All @@ -73,20 +67,17 @@ public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bo
if (!validShortName)
return false;

//
// Subsequent segments must start with a letter or a digit
//

for (; i < end; ++i)
for (; (uint)i < (uint)name.Length; i++)
{
if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
{
end = i;
break;
}
else if (name[i] == '.')
{
if (!validShortName || ((i - 1) >= start && name[i - 1] == '.'))
if (!validShortName || name[i - 1] == '.')
return false;

validShortName = false;
Expand All @@ -98,23 +89,25 @@ public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bo
}
else if (char.IsLetter(name[i]) || char.IsAsciiDigit(name[i]))
{
if (!validShortName)
validShortName = true;
validShortName = true;
}
else
{
return false;
}
}

// last segment can end with the dot
if (((i - 1) >= start && name[i - 1] == '.'))
validShortName = true;

if (!validShortName)
return false;

// caller must check for (end - start <= MaximumInternetNameLength)
{
// last segment can end with the dot
if ((uint)(i - 1) >= (uint)name.Length || name[i - 1] != '.')
{
return false;
}
}

returnedEnd = end;
// Caller must check that (nameLength <= MaximumInternetNameLength)
nameLength = i;
return true;
}
}
Expand Down
Loading
Loading