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

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') | tests/corelib/uri_file_test.dart » ('J')
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..38774759593cffcf33fd3eafe50645e4f62bc0ff 100644
--- a/sdk/lib/core/uri.dart
+++ b/sdk/lib/core/uri.dart
@@ -118,17 +118,183 @@ 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) {
Søren Gjesse 2014/02/24 09:12:41 I think it could be a good idea to include the reg
Anders Johnsen 2014/02/24 12:44:33 Done.
+ // This parsing will not validate percent-encoding, IPv6, etc. When done
+ // it'll call `new Uri(...)` that will perform these validations. This is
Lasse Reichstein Nielsen 2014/02/24 13:02:27 it'll -> it will that -> which
Anders Johnsen 2014/02/24 14:26:49 Done.
+ // purely splitting up the uri string into components.
Lasse Reichstein Nielsen 2014/02/24 13:02:27 uri -> URI.
Anders Johnsen 2014/02/24 14:26:49 Done.
+ bool isSchemeCharacter(int ch) {
Lasse Reichstein Nielsen 2014/02/24 13:02:27 Consider making this a static private method inste
Anders Johnsen 2014/02/24 14:26:49 Done.
+ return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ bool isRegName(int ch) {
+ return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ List<int> codeUnits = uri.codeUnits;
+ int length = codeUnits.length;
+ int index = 0;
+
+ int schemeEndIndex = 0;
+
+ if (length == 0) {
+ return new Uri();
+ }
+
+ if (codeUnits[0] != '/'.codeUnitAt(0)) {
Søren Gjesse 2014/02/24 09:12:41 Use _SLASH (see frequently used character codes at
Anders Johnsen 2014/02/24 12:44:33 Done.
Lasse Reichstein Nielsen 2014/02/24 13:02:27 Unless you *know* that this is optimized away by a
Anders Johnsen 2014/02/24 14:26:49 Any const static members should be compiled away?
+ // Can be scheme.
+ while (index < length) {
Søren Gjesse 2014/02/24 09:12:41 Please provide some more comments, e.g. "Look for
Anders Johnsen 2014/02/24 12:44:33 Done.
+
+ int codeUnit = codeUnits[index++];
+ if (!isSchemeCharacter(codeUnit)) {
+ if (codeUnit == ':'.codeUnitAt(0)) {
Søren Gjesse 2014/02/24 09:12:41 _COLON, and more below.
Anders Johnsen 2014/02/24 12:44:33 Done.
+ schemeEndIndex = index;
+ } else {
+ // Back up one char, as we meet a special char.
Søren Gjesse 2014/02/24 09:12:41 meet -> met?
Anders Johnsen 2014/02/24 12:44:33 Done.
Lasse Reichstein Nielsen 2014/02/24 13:02:27 as -> since
Anders Johnsen 2014/02/24 14:26:49 Done.
+ index--;
+ }
+ break;
+ }
+ }
+ }
+
+ int userInfoEndIndex = -1;
+ int portIndex = -1;
+ int authorityEndIndex = schemeEndIndex;
+ // If we see '//', it must be a authority.
Søren Gjesse 2014/02/24 09:12:41 it must -> there must a -> an
Anders Johnsen 2014/02/24 12:44:33 Done.
+ if (authorityEndIndex == index &&
+ authorityEndIndex + 1 < length &&
+ codeUnits[authorityEndIndex] == '/'.codeUnitAt(0) &&
+ codeUnits[authorityEndIndex + 1] == '/'.codeUnitAt(0)) {
+ // Skip '//'.
+ authorityEndIndex += 2;
+ // It can both be host and userInfo.
+ while (authorityEndIndex < length) {
+ int codeUnit = codeUnits[authorityEndIndex++];
+ if (!isRegName(codeUnit)) {
+ if (codeUnit == '['.codeUnitAt(0)) {
+ // IPv6. Skip to '['.
Søren Gjesse 2014/02/24 09:12:41 '[' -> ']'
Anders Johnsen 2014/02/24 12:44:33 Done.
+ authorityEndIndex = codeUnits.indexOf(']'.codeUnitAt(0),
+ authorityEndIndex) + 1;
+ if (authorityEndIndex == 0) {
+ throw new FormatException("Bad end of IPv6 host");
+ }
+ } else if (portIndex == -1 && codeUnit == ':'.codeUnitAt(0)) {
+ // First time ':'.
+ portIndex = authorityEndIndex;
+ } else if (codeUnit == '@'.codeUnitAt(0) ||
+ codeUnit == ':'.codeUnitAt(0)) {
+ // 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 == '['.codeUnitAt(0)) {
Søren Gjesse 2014/02/24 09:12:41 Refactor next 5 lines to a local function authori
Anders Johnsen 2014/02/24 12:44:33 Done.
+ authorityEndIndex = codeUnits.indexOf(']'.codeUnitAt(0),
+ authorityEndIndex) + 1;
+ if (authorityEndIndex == 0) {
+ throw new FormatException("Bad end of IPv6 host");
+ }
+ } else if (codeUnit == ':'.codeUnitAt(0)) {
+ 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 == '?'.codeUnitAt(0) ||
+ codeUnit == '#'.codeUnitAt(0)) {
+ pathEndIndex--;
+ break;
+ }
+ }
+
+ // Maybe query.
+ int queryEndIndex = pathEndIndex;
+ if (queryEndIndex < length &&
+ codeUnits[queryEndIndex] == '?'.codeUnitAt(0)) {
+ while (queryEndIndex < length) {
+ int codeUnit = codeUnits[queryEndIndex++];
+ if (codeUnit == '#'.codeUnitAt(0)) {
+ 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.
@@ -719,58 +885,6 @@ 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 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.
*/
@@ -1625,6 +1739,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') | tests/corelib/uri_file_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698