Skip to content

Commit

Permalink
Add InternalStyle:rootLength to implement isAbsolute and rootPrefix.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ajohnsen@google.com committed Aug 7, 2014
1 parent 8f2715d commit 27dfe34
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 97 deletions.
9 changes: 3 additions & 6 deletions pkgs/path/lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,7 @@ class Context {
/// 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.
Expand All @@ -165,7 +162,7 @@ class Context {
/// 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
Expand All @@ -181,7 +178,7 @@ class Context {
/// 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:
///
Expand Down
19 changes: 15 additions & 4 deletions pkgs/path/lib/src/internal_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,25 @@ abstract class InternalStyle extends Style {
/// "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);
Expand Down
2 changes: 1 addition & 1 deletion pkgs/path/lib/src/parsed_path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ParsedPath {

// 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.
Expand Down
8 changes: 5 additions & 3 deletions pkgs/path/lib/src/style/posix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ class PosixStyle extends InternalStyle {
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) {
Expand Down
57 changes: 17 additions & 40 deletions pkgs/path/lib/src/style/url.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,53 +36,30 @@ class UrlStyle extends InternalStyle {

// 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);
}
}
73 changes: 30 additions & 43 deletions pkgs/path/lib/src/style/windows.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,38 @@ class WindowsStyle extends InternalStyle {
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) {
Expand Down Expand Up @@ -100,39 +122,4 @@ class WindowsStyle extends InternalStyle {
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);
}
}
}
1 change: 1 addition & 0 deletions pkgs/path/test/url_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ main() {
expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org');
expect(context.rootPrefix('file://'), 'file://');
expect(context.rootPrefix('/'), '/');
expect(context.rootPrefix('foo/bar://'), '');
});

test('dirname', () {
Expand Down
3 changes: 3 additions & 0 deletions pkgs/path/test/windows_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ main() {
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'\');
Expand Down

0 comments on commit 27dfe34

Please sign in to comment.