Skip to content

Conversation

@Forgind
Copy link
Contributor

@Forgind Forgind commented Mar 30, 2021

For some reason, the pre-caching change seems to fail in the installer repo at the build step because of a failing relative path calculation. This version seems to work.

@benvillalobos
Copy link
Member

I assume it's a PR from msbuild into the installer that's failing tests? Can you link it here?

@Forgind
Copy link
Contributor Author

Forgind commented Mar 30, 2021

dotnet/installer#10037

Based on some local testing, I currently believe it needs both this and #6301 to go in for it to work. (I also have a couple local changes.)

Copy link
Member

@benvillalobos benvillalobos left a comment

Choose a reason for hiding this comment

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

First pass review. Any idea what the perf impact is here?

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
return sb.ToString();
return StringBuilderCache.GetStringAndRelease(sb);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was trying to think of that but didn't come up with it. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

This seems suspicious to me, but I have no evidence off the top of my head 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Seeing if fullPath starts with the current working directory could be another way of determining this, though the current method is faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One possible suspicious reason is the (bug) I just found for mac/linux: the path starts with /, which means this will always return false. Now fixed.

Copy link
Member

@ladipro ladipro left a comment

Choose a reason for hiding this comment

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

Can you please add details on path patterns where the old code does not work properly? Thank you!

Copy link
Member

Choose a reason for hiding this comment

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

Calling Split with StringSplitOptions.RemoveEmptyEntries should be a faster way of filtering out empty components.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to make it char[] because it seems that Split(char, StringSplitOptions) isn't available on .NET Framework. I don't think ifdefs are worth it here. Still a lot nicer than what I had before; thanks!

Comment on lines 1043 to 1047
Copy link
Member

Choose a reason for hiding this comment

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

What guarantees that the string is not just slashes, meaning that the code won't crash with out of bounds access? The split above uses only the primary directory separator char on the current platform and will accept e.g. forward slashes on Windows.

@Forgind
Copy link
Contributor Author

Forgind commented Apr 1, 2021

The old code threw exceptions if the URI was deemed invalid for some reason. Two such cases that stood out to me:
If it contains too many slashes.
If the specified scheme name was invalid. "The scheme name must begin with a letter and must contain only letters, digits, and the characters ".", "+", or "-"." That was too restrictive for, among other things, macs.

Copy link
Member

@ladipro ladipro left a comment

Choose a reason for hiding this comment

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

This is

string fullPath = Path.GetFullPath(path);

Uri baseUri = new Uri(EnsureTrailingSlash(basePath), UriKind.Absolute); // May throw UriFormatException
string[] splitBase = fullBase.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
Copy link
Member

Choose a reason for hiding this comment

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

The char array is worth caching. And looks like we already do it in Constants.DirectorySeparatorChar!

sb.Append(splitPath[pathI]).Append(Path.DirectorySeparatorChar);
pathI++;
}
sb.Remove(sb.Length - 1, 1);
Copy link
Member

Choose a reason for hiding this comment

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

nit: I would expect

Suggested change
sb.Remove(sb.Length - 1, 1);
sb.Length = sb.Length - 1;

to be slightly faster and also easier to read.

Comment on lines 1056 to 1079
while (true)
{
if (baseI == splitBase.Length)
{
if (pathI == splitPath.Length)
{
return ".";
}
break;
}
else if (pathI == splitPath.Length)
{
break;
}
else if (splitBase[baseI].Equals(splitPath[pathI], PathComparison))
{
baseI++;
pathI++;
}
else
{
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I would find the following much easier to read. As long as it's equivalent, please double-check:

Suggested change
while (true)
{
if (baseI == splitBase.Length)
{
if (pathI == splitPath.Length)
{
return ".";
}
break;
}
else if (pathI == splitPath.Length)
{
break;
}
else if (splitBase[baseI].Equals(splitPath[pathI], PathComparison))
{
baseI++;
pathI++;
}
else
{
break;
}
}
while (baseI < splitBase.Length && pathI < splitPath.Length && splitBase[baseI].Equals(splitPath[pathI], PathComparison))
{
baseI++;
pathI++;
}
if (baseI == splitBase.Length && pathI == splitPath.Length)
{
return ".";
}
}

Comment on lines 1054 to 1055
int baseI = 0;
int pathI = 0;
Copy link
Member

Choose a reason for hiding this comment

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

nit (last one, promise!): Since the two integers are always incremented together, consider having only one and changing the while (baseI < ... and while (pathI < ... below to for loops. Totally optional, no perf impact, just a matter of subjective taste.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep! This PR came out unusually janky, so I appreciate all the refactoring suggestions.

Copy link
Member

@benvillalobos benvillalobos left a comment

Choose a reason for hiding this comment

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

I like where this PR ended up. I did some rough testing to see which was faster and it looks like this version is 👍

{
return ".";
}
// If the paths have no component in common, the only valid relative path is the full path.
Copy link
Member

Choose a reason for hiding this comment

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

hyper-nit-9000: newline here.

@Forgind
Copy link
Contributor Author

Forgind commented Apr 2, 2021

Thanks @benvillalobos for the perf check!

@Forgind Forgind merged commit 1cfebe4 into dotnet:main Apr 2, 2021
@Forgind Forgind deleted the change-relative-path-calculation branch April 2, 2021 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants