From acbe3b720aa1728ae2b07abeb5a45e60752f3d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 2 Jan 2025 11:58:52 +0100 Subject: [PATCH 1/4] Swap implementation and documentation of GenericPath.normalize(d). --- source/vibe/core/path.d | 50 ++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 6e7e7060..982f13a9 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -723,26 +723,9 @@ struct GenericPath(F) { /** Returns the normalized form of the path. - See `normalize` for a full description. - */ - @property GenericPath normalized() - const { - GenericPath ret = this; - ret.normalize(); - return ret; - } - - unittest { - assert(PosixPath("foo/../bar").normalized == PosixPath("bar")); - assert(PosixPath("foo//./bar/../baz").normalized == PosixPath("foo/baz")); - } - - - /** Removes any redundant path segments and replaces all separators by the - default one. - - The resulting path representation is suitable for basic semantic - comparison to other normalized paths. + This removes any redundant path segments and replaces all separators + by the default one. The resulting path representation is suitable for + basic semantic comparison to other normalized paths. Note that there are still ways for different normalized paths to represent the same file. Examples of this are the tilde shortcut to the @@ -754,8 +737,8 @@ struct GenericPath(F) { segments ("..") that lead to a path that is a parent path of the root path. */ - void normalize() - { + @property GenericPath normalized() + const { import std.array : appender, join; Segment[] newnodes; @@ -779,7 +762,28 @@ struct GenericPath(F) { auto dst = appender!string; Format.toString(newnodes, dst); - m_path = dst.data; + + GenericPath ret; + ret.m_path = dst.data; + return ret; + } + + /// + unittest { + assert(PosixPath("foo/../bar").normalized == PosixPath("bar")); + assert(PosixPath("foo//./bar/../baz").normalized == PosixPath("foo/baz")); + assert(PosixPath("/foo/../bar").normalized == PosixPath("/bar")); + assert(WindowsPath(`\\PC/c$\foo/../bar/`).normalized == WindowsPath(`\\PC\c$\bar\`)); + } + + + /** Replaces the path representation with its normalized form. + + See `normalized` for a full description. + */ + void normalize() + { + this.m_path = this.normalized.m_path; } /// From 452b0261ed7591217b0a6a1c698128a0ac2b4a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 2 Jan 2025 12:09:13 +0100 Subject: [PATCH 2/4] Avoid redundant path construction in GenericPath.normalized. --- source/vibe/core/path.d | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 982f13a9..3e8cebec 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -7,8 +7,8 @@ */ module vibe.core.path; -import std.algorithm.searching : commonPrefix, endsWith, startsWith; -import std.algorithm.comparison : equal, min; +import std.algorithm.searching : any, commonPrefix, endsWith, startsWith; +import std.algorithm.comparison : among, equal, min; import std.algorithm.iteration : map; import std.exception : enforce; import std.range : empty, front, popFront, popFrontExactly, takeExactly; @@ -741,6 +741,13 @@ struct GenericPath(F) { const { import std.array : appender, join; + // avoid constucting a new path if already normalized + if (!this.bySegment.any!(s => s.encodedName.among("", ".", "..") + || (s.hasSeparator && s.separator != Format.defaultSeparator))) + { + return this; + } + Segment[] newnodes; bool got_non_sep = false; foreach (n; this.bySegment) { @@ -774,6 +781,8 @@ struct GenericPath(F) { assert(PosixPath("foo//./bar/../baz").normalized == PosixPath("foo/baz")); assert(PosixPath("/foo/../bar").normalized == PosixPath("/bar")); assert(WindowsPath(`\\PC/c$\foo/../bar/`).normalized == WindowsPath(`\\PC\c$\bar\`)); + assert(PosixPath("/foo/bar").normalized == PosixPath("/foo/bar")); + assert(PosixPath("foo/bar/").normalized == PosixPath("foo/bar/")); } From dc640f638bb857bf2a8b7e1509b29818a8ab7da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 2 Jan 2025 17:36:44 +0100 Subject: [PATCH 3/4] Deprecate GenericPath.Segment2. --- source/vibe/core/file.d | 2 +- source/vibe/core/path.d | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/source/vibe/core/file.d b/source/vibe/core/file.d index 9a76f609..b29c72dc 100644 --- a/source/vibe/core/file.d +++ b/source/vibe/core/file.d @@ -1134,7 +1134,7 @@ private void performListDirectory(ListDirectoryRequest req) if (fi.isSymlink && !req.followSymlinks) continue; try { - if (!scanRec(path ~ NativePath.Segment2(fi.name))) + if (!scanRec(path ~ NativePath.Segment(fi.name))) return false; } catch (Exception e) {} } diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 3e8cebec..e26581d6 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -65,7 +65,7 @@ Path relativeTo(Path)(in Path path, in Path base_path) @safe base++; } - enum up = Path.Segment2("..", Path.defaultSeparator); + enum up = Path.Segment("..", Path.defaultSeparator); auto ret = Path(base_nodes.map!(p => up).chain(nodes)); if (path.endsWithSlash) { if (ret.empty) return Path.fromTrustedString("." ~ path.toString()[$-1]); @@ -216,6 +216,7 @@ struct GenericPath(F) { alias Format = F; /// vibe-core 1.x compatibility alias + deprecated("Use `Segment` instead.") alias Segment2 = Segment; /** A single path segment. @@ -524,6 +525,7 @@ struct GenericPath(F) { } /// vibe-core 1.x compatibility alias + deprecated("Use `.bySegment` instead.") alias bySegment2 = bySegment; /** Iterates over the individual segments of the path. @@ -665,6 +667,7 @@ struct GenericPath(F) { } // vibe-core 1.x compatibility alias + deprecated("Use `.head` instead.") alias head2 = head; /// Returns the trailing segment of the path. From 754d419689db35078dcf27d3b746c86060e73276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 2 Jan 2025 21:08:34 +0100 Subject: [PATCH 4/4] Adjust the description for "normalize" to emphasize "normalized" instead. --- source/vibe/core/path.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index e26581d6..71556f78 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -791,7 +791,8 @@ struct GenericPath(F) { /** Replaces the path representation with its normalized form. - See `normalized` for a full description. + This is a simple convenience wrapper around the functional style + `.normalized`, which is usually preferable. */ void normalize() {