Add InternalStyle:rootLength to implement isAbsolute and rootPrefix.
This is the first step at adding a few helper methods for improving
path package performance.
BUG=
R=nweiz@google.com, rnystrom@google.com
Review URL: https://codereview.chromium.org//439223002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38966 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/src/context.dart b/lib/src/context.dart
index 7a88909..e9237a2 100644
--- a/lib/src/context.dart
+++ b/lib/src/context.dart
@@ -148,10 +148,7 @@
/// context.rootPrefix('path/to/foo'); // -> ''
/// context.rootPrefix('http://dartlang.org/path/to/foo');
/// // -> 'http://dartlang.org'
- String rootPrefix(String path) {
- var root = _parse(path).root;
- return root == null ? '' : root;
- }
+ String rootPrefix(String path) => path.substring(0, style.rootLength(path));
/// Returns `true` if [path] is an absolute path and `false` if it is a
/// relative path.
@@ -165,7 +162,7 @@
/// relative to the root of the current URL. Since root-relative paths are
/// still absolute in every other sense, [isAbsolute] will return true for
/// them. They can be detected using [isRootRelative].
- bool isAbsolute(String path) => _parse(path).isAbsolute;
+ bool isAbsolute(String path) => style.rootLength(path) > 0;
/// Returns `true` if [path] is a relative path and `false` if it is absolute.
/// On POSIX systems, absolute paths start with a `/` (forward slash). On
@@ -181,7 +178,7 @@
/// them. They can be detected using [isRootRelative].
///
/// No POSIX and Windows paths are root-relative.
- bool isRootRelative(String path) => _parse(path).isRootRelative;
+ bool isRootRelative(String path) => style.isRootRelative(path);
/// Joins the given path parts into a single path. Example:
///
diff --git a/lib/src/internal_style.dart b/lib/src/internal_style.dart
index 67b5d34..db2d346 100644
--- a/lib/src/internal_style.dart
+++ b/lib/src/internal_style.dart
@@ -33,14 +33,25 @@
/// "usr", an additional "/" is needed (making "file:///usr").
bool needsSeparator(String path);
+ /// Returns the number of characters of the root part.
+ ///
+ /// Returns 0 if the path is relative.
+ ///
+ /// If the path is root-relative, the root length is 1.
+ int rootLength(String path);
+
/// Gets the root prefix of [path] if path is absolute. If [path] is relative,
/// returns `null`.
- String getRoot(String path);
+ String getRoot(String path) {
+ var length = rootLength(path);
+ if (length > 0) return path.substring(0, length);
+ return isRootRelative(path) ? path[0] : null;
+ }
- /// Gets the root prefix of [path] if it's root-relative.
+ /// Returns whether [path] is root-relative.
///
- /// If [path] is relative or absolute and not root-relative, returns `null`.
- String getRelativeRoot(String path);
+ /// If [path] is relative or absolute and not root-relative, returns `false`.
+ bool isRootRelative(String path);
/// Returns the path represented by [uri] in this style.
String pathFromUri(Uri uri);
diff --git a/lib/src/parsed_path.dart b/lib/src/parsed_path.dart
index 57773ee..a7b0afd 100644
--- a/lib/src/parsed_path.dart
+++ b/lib/src/parsed_path.dart
@@ -45,7 +45,7 @@
// Remove the root prefix, if any.
var root = style.getRoot(path);
- var isRootRelative = style.getRelativeRoot(path) != null;
+ var isRootRelative = style.isRootRelative(path);
if (root != null) path = path.substring(root.length);
// Split the parts on path separators.
diff --git a/lib/src/style/posix.dart b/lib/src/style/posix.dart
index b8b82b4..74aeb4c 100644
--- a/lib/src/style/posix.dart
+++ b/lib/src/style/posix.dart
@@ -30,11 +30,13 @@
bool needsSeparator(String path) =>
path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1));
- String getRoot(String path) {
- if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return '/';
- return null;
+ int rootLength(String path) {
+ if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1;
+ return 0;
}
+ bool isRootRelative(String path) => false;
+
String getRelativeRoot(String path) => null;
String pathFromUri(Uri uri) {
diff --git a/lib/src/style/url.dart b/lib/src/style/url.dart
index f383923..d5d0fdb 100644
--- a/lib/src/style/url.dart
+++ b/lib/src/style/url.dart
@@ -36,53 +36,30 @@
// A URI that's just "scheme://" needs an extra separator, despite ending
// with "/".
- var root = _getRoot(path);
- return root != null && root.endsWith('://');
+ return path.endsWith("://") && rootLength(path) == path.length;
}
- String getRoot(String path) {
- var root = _getRoot(path);
- return root == null ? getRelativeRoot(path) : root;
+ int rootLength(String path) {
+ if (path.isEmpty) return 0;
+ if (isSeparator(path.codeUnitAt(0))) return 1;
+ var index = path.indexOf("/");
+ if (index > 0 && path.startsWith('://', index - 1)) {
+ // The root part is up until the next '/', or the full path. Skip
+ // '://' and search for '/' after that.
+ index = path.indexOf('/', index + 2);
+ if (index > 0) return index;
+ return path.length;
+ }
+ return 0;
}
- String getRelativeRoot(String path) {
- if (path.isEmpty) return null;
- return isSeparator(path.codeUnitAt(0)) ? "/" : null;
- }
+ bool isRootRelative(String path) =>
+ path.isNotEmpty && isSeparator(path.codeUnitAt(0));
+
+ String getRelativeRoot(String path) => isRootRelative(path) ? '/' : null;
String pathFromUri(Uri uri) => uri.toString();
Uri relativePathToUri(String path) => Uri.parse(path);
Uri absolutePathToUri(String path) => Uri.parse(path);
-
- // A helper method for [getRoot] that doesn't handle relative roots.
- String _getRoot(String path) {
- if (path.isEmpty) return null;
-
- // We aren't using a RegExp for this because they're slow (issue 19090). If
- // we could, we'd match against r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*".
-
- if (!isAlphabetic(path.codeUnitAt(0))) return null;
- var start = 1;
- for (; start < path.length; start++) {
- var char = path.codeUnitAt(start);
- if (isAlphabetic(char)) continue;
- if (isNumeric(char)) continue;
- if (char == chars.MINUS || char == chars.PLUS || char == chars.PERIOD) {
- continue;
- }
-
- break;
- }
-
- if (start + 3 > path.length) return null;
- if (path.substring(start, start + 3) != '://') return null;
- start += 3;
-
- // A URL root can end with a non-"/" prefix.
- while (start < path.length && !isSeparator(path.codeUnitAt(start))) {
- start++;
- }
- return path.substring(0, start);
- }
}
diff --git a/lib/src/style/windows.dart b/lib/src/style/windows.dart
index 2965f1e..16e14d5 100644
--- a/lib/src/style/windows.dart
+++ b/lib/src/style/windows.dart
@@ -34,16 +34,38 @@
return !isSeparator(path.codeUnitAt(path.length - 1));
}
- String getRoot(String path) {
- var root = _getRoot(path);
- return root == null ? getRelativeRoot(path) : root;
+ int rootLength(String path) {
+ if (path.isEmpty) return 0;
+ if (path.codeUnitAt(0) == chars.SLASH) return 1;
+ if (path.codeUnitAt(0) == chars.BACKSLASH) {
+ if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1;
+ // The path is a network share. Search for up to two '\'s, as they are
+ // the server and share - and part of the root part.
+ var index = path.indexOf('\\', 2);
+ if (index > 0) {
+ index = path.indexOf('\\', index + 1);
+ if (index > 0) return index;
+ }
+ return path.length;
+ }
+ // If the path is of the form 'C:/' or 'C:\', with C being any letter, it's
+ // a root part.
+ if (path.length < 3) return 0;
+ // Check for the letter.
+ if (!isAlphabetic(path.codeUnitAt(0))) return 0;
+ // Check for the ':'.
+ if (path.codeUnitAt(1) != chars.COLON) return 0;
+ // Check for either '/' or '\'.
+ if (!isSeparator(path.codeUnitAt(2))) return 0;
+ return 3;
}
+ bool isRootRelative(String path) => rootLength(path) == 1;
+
String getRelativeRoot(String path) {
- if (path.isEmpty) return null;
- if (!isSeparator(path.codeUnitAt(0))) return null;
- if (path.length > 1 && isSeparator(path.codeUnitAt(1))) return null;
- return path[0];
+ var length = rootLength(path);
+ if (length == 1) return path[0];
+ return null;
}
String pathFromUri(Uri uri) {
@@ -100,39 +122,4 @@
return new Uri(scheme: 'file', pathSegments: parsed.parts);
}
}
-
- // A helper method for [getRoot] that doesn't handle relative roots.
- String _getRoot(String path) {
- if (path.length < 3) return null;
-
- // We aren't using a RegExp for this because they're slow (issue 19090). If
- // we could, we'd match against r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'.
-
- // Try roots like "C:\".
- if (isAlphabetic(path.codeUnitAt(0))) {
- if (path.codeUnitAt(1) != chars.COLON) return null;
- if (!isSeparator(path.codeUnitAt(2))) return null;
- return path.substring(0, 3);
- }
-
- // Try roots like "\\server\share".
- if (!path.startsWith('\\\\')) return null;
-
- var start = 2;
- // The server is one or more non-"\" characters.
- while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) {
- start++;
- }
- if (start == 2 || start == path.length) return null;
-
- // The share is one or more non-"\" characters.
- start += 1;
- if (path.codeUnitAt(start) == chars.BACKSLASH) return null;
- start += 1;
- while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) {
- start++;
- }
-
- return path.substring(0, start);
- }
-}
\ No newline at end of file
+}
diff --git a/test/url_test.dart b/test/url_test.dart
index 27691d8..d75377e 100644
--- a/test/url_test.dart
+++ b/test/url_test.dart
@@ -37,6 +37,7 @@
expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org');
expect(context.rootPrefix('file://'), 'file://');
expect(context.rootPrefix('/'), '/');
+ expect(context.rootPrefix('foo/bar://'), '');
});
test('dirname', () {
diff --git a/test/windows_test.dart b/test/windows_test.dart
index 7c16e31..72eefc3 100644
--- a/test/windows_test.dart
+++ b/test/windows_test.dart
@@ -41,6 +41,9 @@
expect(context.rootPrefix('C:\\'), r'C:\');
expect(context.rootPrefix('C:/'), 'C:/');
expect(context.rootPrefix(r'\\server\share\a\b'), r'\\server\share');
+ expect(context.rootPrefix(r'\\server\share'), r'\\server\share');
+ expect(context.rootPrefix(r'\\server\'), r'\\server\');
+ expect(context.rootPrefix(r'\\server'), r'\\server');
expect(context.rootPrefix(r'\a\b'), r'\');
expect(context.rootPrefix(r'/a/b'), r'/');
expect(context.rootPrefix(r'\'), r'\');