Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Unified Diff: sdk/lib/core/uri.dart

Issue 167703010: Write custom URI-parser, to avoid using regexp. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/corelib/uri_file_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/core/uri.dart
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
index 41130ad08cea677b05826f3f4574e48842324bf7..1f45ed2fa5ab136eb887ab838cf0d001cb79d220 100644
--- a/sdk/lib/core/uri.dart
+++ b/sdk/lib/core/uri.dart
@@ -118,17 +118,229 @@ class Uri {
/**
* Creates a new URI object by parsing a URI string.
*/
- static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
-
- Uri._fromMatch(Match m) :
- this(scheme: _makeScheme(_emptyIfNull(m[_COMPONENT_SCHEME])),
- userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
- host: _eitherOf(
- m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]),
- port: _parseIntOrZero(m[_COMPONENT_PORT]),
- path: _emptyIfNull(m[_COMPONENT_PATH]),
- query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
- fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT]));
+ static Uri parse(String uri) {
+ // This parsing will not validate percent-encoding, IPv6, etc. When done
+ // it will call `new Uri(...)` which will perform these validations.
+ // This is purely splitting up the URI string into components.
+ //
+ // Important parts of the RFC 3986 used here:
+ // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ //
+ // hier-part = "//" authority path-abempty
+ // / path-absolute
+ // / path-rootless
+ // / path-empty
+ //
+ // URI-reference = URI / relative-ref
+ //
+ // absolute-URI = scheme ":" hier-part [ "?" query ]
+ //
+ // relative-ref = relative-part [ "?" query ] [ "#" fragment ]
+ //
+ // relative-part = "//" authority path-abempty
+ // / path-absolute
+ // / path-noscheme
+ // / path-empty
+ //
+ // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ //
+ // authority = [ userinfo "@" ] host [ ":" port ]
+ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+ // host = IP-literal / IPv4address / reg-name
+ // port = *DIGIT
+ // reg-name = *( unreserved / pct-encoded / sub-delims )
+ //
+ // path = path-abempty ; begins with "/" or is empty
+ // / path-absolute ; begins with "/" but not "//"
+ // / path-noscheme ; begins with a non-colon segment
+ // / path-rootless ; begins with a segment
+ // / path-empty ; zero characters
+ //
+ // path-abempty = *( "/" segment )
+ // path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ // path-noscheme = segment-nz-nc *( "/" segment )
+ // path-rootless = segment-nz *( "/" segment )
+ // path-empty = 0<pchar>
+ //
+ // segment = *pchar
+ // segment-nz = 1*pchar
+ // segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ // ; non-zero-length segment without any colon ":"
+ //
+ // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ //
+ // query = *( pchar / "/" / "?" )
+ //
+ // fragment = *( pchar / "/" / "?" )
+ bool isRegName(int ch) {
+ return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ int ipV6Address(List<int> codeUnits, int index) {
+ // IPv6. Skip to ']'.
+ index = codeUnits.indexOf(_RIGHT_BRACKET, index);
+ if (index == -1) {
+ throw new FormatException("Bad end of IPv6 host");
+ }
+ return index + 1;
+ }
+
+ List<int> codeUnits = uri.codeUnits;
+ int length = codeUnits.length;
+ int index = 0;
+
+ int schemeEndIndex = 0;
+
+ if (length == 0) {
+ return new Uri();
+ }
+
+ if (codeUnits[0] != _SLASH) {
+ // Can be scheme.
+ while (index < length) {
+ // Look for ':'. If found, continue from the post of ':'. If not (end
+ // reached or invalid scheme char found) back up one char, and continue
+ // to path.
+ // Note that scheme-chars is contained in path-chars.
+ int codeUnit = codeUnits[index++];
+ if (!_isSchemeCharacter(codeUnit)) {
+ if (codeUnit == _COLON) {
+ schemeEndIndex = index;
+ } else {
+ // Back up one char, since we met an invalid scheme char.
+ index--;
+ }
+ break;
+ }
+ }
+ }
+
+ int userInfoEndIndex = -1;
+ int portIndex = -1;
+ int authorityEndIndex = schemeEndIndex;
+ // If we see '//', there must be an authority.
+ if (authorityEndIndex == index &&
+ authorityEndIndex + 1 < length &&
+ codeUnits[authorityEndIndex] == _SLASH &&
+ codeUnits[authorityEndIndex + 1] == _SLASH) {
+ // Skip '//'.
+ authorityEndIndex += 2;
+ // It can both be host and userInfo.
+ while (authorityEndIndex < length) {
+ int codeUnit = codeUnits[authorityEndIndex++];
+ if (!isRegName(codeUnit)) {
+ if (codeUnit == _LEFT_BRACKET) {
+ authorityEndIndex = ipV6Address(codeUnits, authorityEndIndex);
+ } else if (portIndex == -1 && codeUnit == _COLON) {
+ // First time ':'.
+ portIndex = authorityEndIndex;
+ } else if (codeUnit == _AT_SIGN || codeUnit == _COLON) {
+ // Second time ':' or first '@'. Must be userInfo.
+ userInfoEndIndex = codeUnits.indexOf('@'.codeUnitAt(0),
+ authorityEndIndex - 1);
+ // Not found. Must be path then.
+ if (userInfoEndIndex == -1) {
+ authorityEndIndex = index;
+ break;
+ }
+ portIndex = -1;
+ authorityEndIndex = userInfoEndIndex + 1;
+ // Now it can only be host:port.
+ while (authorityEndIndex < length) {
+ int codeUnit = codeUnits[authorityEndIndex++];
+ if (!isRegName(codeUnit)) {
+ if (codeUnit == _LEFT_BRACKET) {
+ authorityEndIndex = ipV6Address(codeUnits, authorityEndIndex);
+ } else if (codeUnit == _COLON) {
+ if (portIndex != -1) {
+ throw new FormatException("Double port in host");
+ }
+ portIndex = authorityEndIndex;
+ } else {
+ authorityEndIndex--;
+ break;
+ }
+ }
+ }
+ break;
+ } else {
+ authorityEndIndex--;
+ break;
+ }
+ }
+ }
+ } else {
+ authorityEndIndex = schemeEndIndex;
+ }
+
+ // At path now.
+ int pathEndIndex = authorityEndIndex;
+ while (pathEndIndex < length) {
+ int codeUnit = codeUnits[pathEndIndex++];
+ if (codeUnit == _QUESTION || codeUnit == _NUMBER_SIGN) {
+ pathEndIndex--;
+ break;
+ }
+ }
+
+ // Maybe query.
+ int queryEndIndex = pathEndIndex;
+ if (queryEndIndex < length && codeUnits[queryEndIndex] == _QUESTION) {
+ while (queryEndIndex < length) {
+ int codeUnit = codeUnits[queryEndIndex++];
+ if (codeUnit == _NUMBER_SIGN) {
+ queryEndIndex--;
+ break;
+ }
+ }
+ }
+
+ var scheme = null;
+ if (schemeEndIndex > 0) {
+ scheme = uri.substring(0, schemeEndIndex - 1);
+ }
+
+ var host = "";
+ var userInfo = "";
+ var port = 0;
+ if (schemeEndIndex != authorityEndIndex) {
+ int startIndex = schemeEndIndex + 2;
+ if (userInfoEndIndex > 0) {
+ userInfo = uri.substring(startIndex, userInfoEndIndex);
+ startIndex = userInfoEndIndex + 1;
+ }
+ if (portIndex > 0) {
+ var portStr = uri.substring(portIndex, authorityEndIndex);
+ try {
+ port = int.parse(portStr);
+ } catch (_) {
+ throw new FormatException("Invalid port: '$portStr'");
+ }
+ host = uri.substring(startIndex, portIndex - 1);
+ } else {
+ host = uri.substring(startIndex, authorityEndIndex);
+ }
+ }
+
+ var path = uri.substring(authorityEndIndex, pathEndIndex);
+ var query = "";
+ if (pathEndIndex < queryEndIndex) {
+ query = uri.substring(pathEndIndex + 1, queryEndIndex);
+ }
+ var fragment = "";
+ // If queryEndIndex is not at end (length), there is a fragment.
+ if (queryEndIndex < length) {
+ fragment = uri.substring(queryEndIndex + 1, length);
+ }
+
+ return new Uri(scheme: scheme,
+ userInfo: userInfo,
+ host: host,
+ port: port,
+ path: path,
+ query: query,
+ fragment: fragment);
+ }
/**
* Creates a new URI from its components.
@@ -545,10 +757,6 @@ class Uri {
((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
}
- bool isSchemeCharacter(int ch) {
- return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
- }
-
if (scheme == null) return "";
bool allLowercase = true;
int length = scheme.length;
@@ -559,7 +767,7 @@ class Uri {
throw new ArgumentError('Illegal scheme: $scheme');
}
if (!isSchemeLowerCharacter(codeUnit)) {
- if (isSchemeCharacter(codeUnit)) {
+ if (_isSchemeCharacter(codeUnit)) {
allLowercase = false;
} else {
throw new ArgumentError('Illegal scheme: $scheme');
@@ -719,57 +927,10 @@ class Uri {
return result.toString();
}
- static String _emptyIfNull(String val) => val != null ? val : '';
-
- static int _parseIntOrZero(String val) {
- if (val != null && val != '') {
- return int.parse(val);
- } else {
- return 0;
- }
+ static bool _isSchemeCharacter(int ch) {
+ return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
}
- static String _eitherOf(String val1, String val2) {
- if (val1 != null) return val1;
- if (val2 != null) return val2;
- return '';
- }
-
- // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
- static final RegExp _splitRe = new RegExp(
- '^'
- '(?:'
- '([^:/?#]+)' // scheme - ignore special characters
- // used by other URL parts such as :,
- // ?, /, #, and .
- ':)?'
- '(?://'
- '(?:([^/?#]*)@)?' // userInfo
- '(?:'
- r'([\w\d\-\u0100-\uffff.%]*)'
- // host - restrict to letters,
- // digits, dashes, dots, percent
- // escapes, and unicode characters.
- '|'
- // TODO(ajohnsen): Only allow a max number of parts?
- r'\[([A-Fa-f0-9:.]*)\])'
- // IPv6 host - restrict to hex,
- // dot and colon.
- '(?::([0-9]+))?' // port
- ')?'
- r'([^?#[]+)?' // path
- r'(?:\?([^#]*))?' // query
- '(?:#(.*))?' // fragment
- r'$');
-
- static const _COMPONENT_SCHEME = 1;
- static const _COMPONENT_USER_INFO = 2;
- static const _COMPONENT_HOST = 3;
- static const _COMPONENT_HOST_IPV6 = 4;
- static const _COMPONENT_PORT = 5;
- static const _COMPONENT_PATH = 6;
- static const _COMPONENT_QUERY_DATA = 7;
- static const _COMPONENT_FRAGMENT = 8;
/**
* Returns whether the URI is absolute.
@@ -1369,6 +1530,7 @@ class Uri {
// Frequently used character codes.
static const int _SPACE = 0x20;
static const int _DOUBLE_QUOTE = 0x22;
+ static const int _NUMBER_SIGN = 0x23;
static const int _PERCENT = 0x25;
static const int _ASTERISK = 0x2A;
static const int _PLUS = 0x2B;
@@ -1625,6 +1787,27 @@ class Uri {
// pqrstuvwxyz ~
0x47ff]; // 0x70 - 0x7f 1111111111100010
+ // Characters allowed in the reg-name as of RFC 3986.
+ // RFC 3986 Apendix A
+ // reg-name = *( unreserved / pct-encoded / sub-delims )
+ static const _regNameTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! $%&'()*+,-.
+ 0x7ff2, // 0x20 - 0x2f 0100111111111110
+ // 0123456789 ; =
+ 0x2bff, // 0x30 - 0x3f 1111111111010100
+ // ABCDEFGHIJKLMNO
+ 0xfffe, // 0x40 - 0x4f 0111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
// Characters allowed in the path as of RFC 3986.
// RFC 3986 section 3.3.
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
« no previous file with comments | « no previous file | tests/corelib/uri_file_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698