From 75c1b14ecd90ce6ab0735a11d7e64c943f39e8c3 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Mon, 19 Mar 2018 12:54:40 +0100 Subject: [PATCH 1/7] use existing api --- src/mscorlib/shared/System/IO/Path.cs | 92 +++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index 1e40ab5e602f..dd30a911ea66 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -51,7 +51,8 @@ public static string ChangeExtension(string path, string extension) s = path.Substring(0, i); break; } - if (PathInternal.IsDirectorySeparator(ch)) break; + if (PathInternal.IsDirectorySeparator(ch)) + break; } if (extension != null && path.Length != 0) @@ -110,7 +111,8 @@ private static int GetDirectoryNameOffset(ReadOnlySpan path) if (end <= rootLength) return -1; - while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])); + while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])) + ; // Trim off any remaining separators (to deal with C:\foo\\bar) while (end > rootLength && PathInternal.IsDirectorySeparator(path[end - 1])) @@ -409,6 +411,81 @@ public static string Join(ReadOnlySpan path1, ReadOnlySpan path2, Re return JoinInternal(path1, path2, path3); } + public static bool TryGetTempPath(Span destination, out int charsWritten) + { + charsWritten = 0; + + var tmpPath = Path.GetTempPath(); + + if (tmpPath.Length > destination.Length) + return false; + + new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); + charsWritten = tmpPath.Length; + + return true; + } + + public static bool TryGetRandomFileName(Span destination, out int charsWritten) + { + charsWritten = 0; + + var tmpPath = Path.GetRandomFileName(); + + if (tmpPath.Length > destination.Length) + return false; + + new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); + charsWritten = tmpPath.Length; + + return true; + } + + public static bool TryGetRelativePath(ReadOnlySpan relativeTo, ReadOnlySpan path, Span destination, out int charsWritten) + { + charsWritten = 0; + + var tmpPath = Path.GetRelativePath(relativeTo.ToString(), path.ToString()); + + if (tmpPath.Length > destination.Length) + return false; + + new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); + charsWritten = tmpPath.Length; + + return true; + } + + public static bool TryGetFullPath(ReadOnlySpan path, Span destination, out int charsWritten) + { + charsWritten = 0; + + var tmpPath = Path.GetFullPath(path.ToString()); + + if (tmpPath.Length > destination.Length) + return false; + + new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); + charsWritten = tmpPath.Length; + + return true; + } + + public static bool TryGetFullPath(ReadOnlySpan path, ReadOnlySpan basePath, Span destination, out int charsWritten) + { + charsWritten = 0; + + var tmpPath = Path.GetFullPath(path.ToString(), basePath.ToString()); + + if (tmpPath.Length > destination.Length) + return false; + + new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); + charsWritten = tmpPath.Length; + + return true; + } + public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten) { charsWritten = 0; @@ -595,7 +672,7 @@ private static unsafe string JoinInternal(ReadOnlySpan first, ReadOnlySpan return string.Create( first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, - Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength:fourth.Length, + Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength: fourth.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator, FourthHasSeparator: fourthHasSeparator), (destination, state) => { @@ -686,8 +763,10 @@ public static string GetRelativePath(string relativeTo, string path) private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { - if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); - if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); + if (string.IsNullOrEmpty(relativeTo)) + throw new ArgumentNullException(nameof(relativeTo)); + if (PathInternal.IsEffectivelyEmpty(path)) + throw new ArgumentNullException(nameof(path)); Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); @@ -714,7 +793,8 @@ private static string GetRelativePath(string relativeTo, string path, StringComp pathLength--; // If we have effectively the same path, return "." - if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; + if (relativeToLength == pathLength && commonLength >= relativeToLength) + return "."; // We have the same root, we need to calculate the difference now using the // common Length and Segment count past the length. From f99a46f50820268c7759e5997ffcbd84c28e4fb0 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 20 Mar 2018 22:46:50 +0100 Subject: [PATCH 2/7] wip --- src/mscorlib/shared/System/IO/Path.cs | 92 ++------------------------- 1 file changed, 6 insertions(+), 86 deletions(-) diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index dd30a911ea66..1e40ab5e602f 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -51,8 +51,7 @@ public static string ChangeExtension(string path, string extension) s = path.Substring(0, i); break; } - if (PathInternal.IsDirectorySeparator(ch)) - break; + if (PathInternal.IsDirectorySeparator(ch)) break; } if (extension != null && path.Length != 0) @@ -111,8 +110,7 @@ private static int GetDirectoryNameOffset(ReadOnlySpan path) if (end <= rootLength) return -1; - while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])) - ; + while (end > rootLength && !PathInternal.IsDirectorySeparator(path[--end])); // Trim off any remaining separators (to deal with C:\foo\\bar) while (end > rootLength && PathInternal.IsDirectorySeparator(path[end - 1])) @@ -411,81 +409,6 @@ public static string Join(ReadOnlySpan path1, ReadOnlySpan path2, Re return JoinInternal(path1, path2, path3); } - public static bool TryGetTempPath(Span destination, out int charsWritten) - { - charsWritten = 0; - - var tmpPath = Path.GetTempPath(); - - if (tmpPath.Length > destination.Length) - return false; - - new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); - charsWritten = tmpPath.Length; - - return true; - } - - public static bool TryGetRandomFileName(Span destination, out int charsWritten) - { - charsWritten = 0; - - var tmpPath = Path.GetRandomFileName(); - - if (tmpPath.Length > destination.Length) - return false; - - new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); - charsWritten = tmpPath.Length; - - return true; - } - - public static bool TryGetRelativePath(ReadOnlySpan relativeTo, ReadOnlySpan path, Span destination, out int charsWritten) - { - charsWritten = 0; - - var tmpPath = Path.GetRelativePath(relativeTo.ToString(), path.ToString()); - - if (tmpPath.Length > destination.Length) - return false; - - new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); - charsWritten = tmpPath.Length; - - return true; - } - - public static bool TryGetFullPath(ReadOnlySpan path, Span destination, out int charsWritten) - { - charsWritten = 0; - - var tmpPath = Path.GetFullPath(path.ToString()); - - if (tmpPath.Length > destination.Length) - return false; - - new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); - charsWritten = tmpPath.Length; - - return true; - } - - public static bool TryGetFullPath(ReadOnlySpan path, ReadOnlySpan basePath, Span destination, out int charsWritten) - { - charsWritten = 0; - - var tmpPath = Path.GetFullPath(path.ToString(), basePath.ToString()); - - if (tmpPath.Length > destination.Length) - return false; - - new Span(ref tmpPath.GetRawStringData(), tmpPath.Length).CopyTo(destination); - charsWritten = tmpPath.Length; - - return true; - } - public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten) { charsWritten = 0; @@ -672,7 +595,7 @@ private static unsafe string JoinInternal(ReadOnlySpan first, ReadOnlySpan return string.Create( first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, - Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength: fourth.Length, + Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength:fourth.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator, FourthHasSeparator: fourthHasSeparator), (destination, state) => { @@ -763,10 +686,8 @@ public static string GetRelativePath(string relativeTo, string path) private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { - if (string.IsNullOrEmpty(relativeTo)) - throw new ArgumentNullException(nameof(relativeTo)); - if (PathInternal.IsEffectivelyEmpty(path)) - throw new ArgumentNullException(nameof(path)); + if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); + if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); @@ -793,8 +714,7 @@ private static string GetRelativePath(string relativeTo, string path, StringComp pathLength--; // If we have effectively the same path, return "." - if (relativeToLength == pathLength && commonLength >= relativeToLength) - return "."; + if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; // We have the same root, we need to calculate the difference now using the // common Length and Segment count past the length. From a0240582979a6a02e761f037f21717b26bc19e94 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 20 Mar 2018 22:46:54 +0100 Subject: [PATCH 3/7] wip --- src/mscorlib/shared/System/IO/Path.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index 1e40ab5e602f..9a5f25c635fe 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -477,6 +477,27 @@ public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, R return true; } + public static bool TryGetTempPath(Span destination, out int charsWritten) + { + charsWritten = 0; + var tmpPath = Path.GetTempPath(); + + if (tmpPath.Length > destination.Length) + return false; + + charsWritten = tmpPath.Length; + + tmpPath.AsSpan().CopyTo(destination); + + return true; + + //var builder = new ValueStringBuilder(); + //GetTempPath(ref builder); + + //return builder.TryCopyTo(destination, out charsWritten); + + } + private static string CombineInternal(string first, string second) { if (string.IsNullOrEmpty(first)) From 5ef6494ade20172c84a72c55d78caef4eb223d15 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 21 Mar 2018 13:30:17 +0100 Subject: [PATCH 4/7] wip --- src/mscorlib/shared/System/IO/Path.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index 9a5f25c635fe..f7c69286df7d 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -478,24 +478,19 @@ public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, R } public static bool TryGetTempPath(Span destination, out int charsWritten) - { + { charsWritten = 0; + var tmpPath = Path.GetTempPath(); if (tmpPath.Length > destination.Length) - return false; - - charsWritten = tmpPath.Length; + return false; tmpPath.AsSpan().CopyTo(destination); - return true; - - //var builder = new ValueStringBuilder(); - //GetTempPath(ref builder); - - //return builder.TryCopyTo(destination, out charsWritten); + charsWritten = tmpPath.Length; + return true; } private static string CombineInternal(string first, string second) From 81bbed6ff26f030d2973fe468f01bd2bc799a28e Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 21 Mar 2018 19:35:51 +0100 Subject: [PATCH 5/7] address PR feedback --- src/mscorlib/shared/System/IO/Path.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index f7c69286df7d..a206e1c568ae 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -480,16 +480,13 @@ public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, R public static bool TryGetTempPath(Span destination, out int charsWritten) { charsWritten = 0; - - var tmpPath = Path.GetTempPath(); + string tmpPath = Path.GetTempPath(); if (tmpPath.Length > destination.Length) return false; tmpPath.AsSpan().CopyTo(destination); - charsWritten = tmpPath.Length; - return true; } From 54d0ee2777d9fb1aaacdb0c360615e673ae88fc4 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Thu, 22 Mar 2018 16:05:57 +0100 Subject: [PATCH 6/7] non allocating string --- src/mscorlib/shared/System/IO/Path.Unix.cs | 29 +++++++++++++++++++ src/mscorlib/shared/System/IO/Path.cs | 13 +++------ .../shared/System/Text/ValueStringBuilder.cs | 2 ++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/mscorlib/shared/System/IO/Path.Unix.cs b/src/mscorlib/shared/System/IO/Path.Unix.cs index f364b84df074..ebe48ddb9df2 100644 --- a/src/mscorlib/shared/System/IO/Path.Unix.cs +++ b/src/mscorlib/shared/System/IO/Path.Unix.cs @@ -69,6 +69,35 @@ private static string RemoveLongPathPrefix(string path) return path; // nop. There's nothing special about "long" paths on Unix. } + private static void GetTempPath(ref ValueStringBuilder builder) + { + const string TempEnvVar = "TMPDIR"; + const string DefaultTempPath = "/tmp/"; + + // Get the temp path from the TMPDIR environment variable. + int requiredSize = 0; + while ((requiredSize = Microsoft.Win32.Win32Native.GetEnvironmentVariable(TempEnvVar, builder.Span)) > builder.Capacity) + { + // Reported size is greater than the buffer size. Increase the capacity. + builder.EnsureCapacity(checked((int)requiredSize)); + } + + if (requiredSize == 0 && Runtime.InteropServices.Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND) + { + builder.EnsureCapacity(DefaultTempPath.Length); + // If it's not set, just return the default path. + builder.Append(DefaultTempPath.AsSpan()); + } + else + { + // If it is, return it, ensuring it ends with a slash. + if (!PathInternal.IsDirectorySeparator(builder[builder.Length - 1])) + { + builder.Append(PathInternal.DirectorySeparatorChar); + } + } + } + public static string GetTempPath() { const string TempEnvVar = "TMPDIR"; diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index a206e1c568ae..3d7d1fe2c4e9 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -478,16 +478,11 @@ public static bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, R } public static bool TryGetTempPath(Span destination, out int charsWritten) - { + { charsWritten = 0; - string tmpPath = Path.GetTempPath(); - - if (tmpPath.Length > destination.Length) - return false; - - tmpPath.AsSpan().CopyTo(destination); - charsWritten = tmpPath.Length; - return true; + ValueStringBuilder vsb = new ValueStringBuilder(); + Path.GetTempPath(ref vsb); + return vsb.TryCopyTo(destination, out charsWritten); } private static string CombineInternal(string first, string second) diff --git a/src/mscorlib/shared/System/Text/ValueStringBuilder.cs b/src/mscorlib/shared/System/Text/ValueStringBuilder.cs index b1abdd59bc65..7e81057df17b 100644 --- a/src/mscorlib/shared/System/Text/ValueStringBuilder.cs +++ b/src/mscorlib/shared/System/Text/ValueStringBuilder.cs @@ -34,6 +34,8 @@ public int Length public int Capacity => _chars.Length; + public Span Span => _chars; + public void EnsureCapacity(int capacity) { if (capacity > _chars.Length) From 2b23a9741820aede0251a5d36a83719f6e69eabf Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Thu, 22 Mar 2018 16:33:39 +0100 Subject: [PATCH 7/7] remove unuseful checked --- src/mscorlib/shared/System/IO/Path.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mscorlib/shared/System/IO/Path.Unix.cs b/src/mscorlib/shared/System/IO/Path.Unix.cs index ebe48ddb9df2..b5c9e09fae39 100644 --- a/src/mscorlib/shared/System/IO/Path.Unix.cs +++ b/src/mscorlib/shared/System/IO/Path.Unix.cs @@ -79,7 +79,7 @@ private static void GetTempPath(ref ValueStringBuilder builder) while ((requiredSize = Microsoft.Win32.Win32Native.GetEnvironmentVariable(TempEnvVar, builder.Span)) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. - builder.EnsureCapacity(checked((int)requiredSize)); + builder.EnsureCapacity(requiredSize); } if (requiredSize == 0 && Runtime.InteropServices.Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND)