From 4bb5fc9ce83aef4b564a5365a233fb834359c492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Mon, 18 Jan 2021 14:42:50 +0100 Subject: [PATCH 1/4] Optmize InetPathFormat.validatePath. Speeds up the check by roughly 4x. --- source/vibe/core/path.d | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 514d007..3944f24 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -1736,13 +1736,13 @@ struct InetPathFormat { static string validatePath(string path) @nogc { for (size_t i = 0; i < path.length; i++) { + if (isAsciiAlphaNum(path[i])) + continue; + switch (path[i]) { default: return "Invalid character in internet path."; // unreserved - case 'A': .. case 'Z': - case 'a': .. case 'z': - case '0': .. case '9': case '-', '.', '_', '~': // subdelims case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': @@ -2029,6 +2029,25 @@ unittest { // test range based path assert(stripExtension(InetPath("foo.bar.txt").head2.name).equal("foo.bar")); } +private static bool isAsciiAlphaNum(char ch) +@safe nothrow pure @nogc { + return (uint(ch) & 0xDF) - 0x41 < 26 || uint(ch) - '0' <= 9; +} + +unittest { + assert(!isAsciiAlphaNum('@')); + assert(isAsciiAlphaNum('A')); + assert(isAsciiAlphaNum('Z')); + assert(!isAsciiAlphaNum('[')); + assert(!isAsciiAlphaNum('`')); + assert(isAsciiAlphaNum('a')); + assert(isAsciiAlphaNum('z')); + assert(!isAsciiAlphaNum('{')); + assert(!isAsciiAlphaNum('/')); + assert(isAsciiAlphaNum('0')); + assert(isAsciiAlphaNum('9')); + assert(!isAsciiAlphaNum(':')); +} unittest { // regression tests assert(NativePath("").bySegment.empty); From dc0fe5ad73a35f7f68fc8f4ea961e44ef1b5467f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Mon, 18 Jan 2021 14:50:57 +0100 Subject: [PATCH 2/4] Apply the same optimization to InetPathFormat.encodeSegment. --- source/vibe/core/path.d | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 3944f24..093bb3a 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -1881,15 +1881,13 @@ struct InetPathFormat { import std.array : appender; foreach (i, char c; segment) { + if (isAsciiAlphaNum(c)) continue; switch (c) { default: auto ret = appender!string; ret.put(segment[0 .. i]); encodeSegment(ret, segment[i .. $]); return ret.data; - case 'a': .. case 'z': - case 'A': .. case 'Z': - case '0': .. case '9': case '-', '.', '_', '~': case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': case ':', '@': From 246b5262c6c05c796f22d0f68a98d0810141ec61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Tue, 19 Jan 2021 10:29:24 +0100 Subject: [PATCH 3/4] Avoid among switch-case jump table overhead for just two possible values. --- source/vibe/core/path.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index 093bb3a..f2eff9c 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -1303,8 +1303,7 @@ struct WindowsPathFormat { static bool isSeparator(dchar ch) @nogc { - import std.algorithm.comparison : among; - return ch.among!('\\', '/') != 0; + return ch == '\\' || ch == '/'; } static string getAbsolutePrefix(string path) From 9f730491961e0b2847cfc2263d63c7515c6a81fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Tue, 19 Jan 2021 10:34:22 +0100 Subject: [PATCH 4/4] Use path format specific separator for root prefix segment. Fixes format invariant checks for formats that do not use "/" as a path separator. --- source/vibe/core/path.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/vibe/core/path.d b/source/vibe/core/path.d index f2eff9c..4190075 100644 --- a/source/vibe/core/path.d +++ b/source/vibe/core/path.d @@ -333,7 +333,7 @@ struct GenericPath(F) { if (m_path.length) { auto ap = Format.getAbsolutePrefix(m_path); if (ap.length && !Format.isSeparator(ap[0])) - m_front = Segment.fromTrustedString(null, '/'); + m_front = Segment.fromTrustedString(null, Format.defaultSeparator); else readFront(); } } @@ -677,7 +677,7 @@ struct GenericPath(F) { if (m_path.length) { auto ap = Format.getAbsolutePrefix(m_path); if (ap.length && !Format.isSeparator(ap[0])) - m_front = Segment2.fromTrustedEncodedString(null, '/'); + m_front = Segment2.fromTrustedEncodedString(null, Format.defaultSeparator); else readFront(); } }