Chromium Code Reviews| 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 / ":" / "@" |