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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/corelib/uri_file_test.dart » ('j') | tests/corelib/uri_file_test.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.core; 5 part of dart.core;
6 6
7 /** 7 /**
8 * A parsed URI, such as a URL. 8 * A parsed URI, such as a URL.
9 * 9 *
10 * **See also:** 10 * **See also:**
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 List<String> _pathSegments; 111 List<String> _pathSegments;
112 112
113 /** 113 /**
114 * Cache the computed return value of [queryParameters]. 114 * Cache the computed return value of [queryParameters].
115 */ 115 */
116 Map<String, String> _queryParameters; 116 Map<String, String> _queryParameters;
117 117
118 /** 118 /**
119 * Creates a new URI object by parsing a URI string. 119 * Creates a new URI object by parsing a URI string.
120 */ 120 */
121 static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri)); 121 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.
122 // This parsing will not validate percent-encoding, IPv6, etc. When done
123 // 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.
124 // 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.
125 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.
126 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
127 }
122 128
123 Uri._fromMatch(Match m) : 129 bool isRegName(int ch) {
124 this(scheme: _makeScheme(_emptyIfNull(m[_COMPONENT_SCHEME])), 130 return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
125 userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]), 131 }
126 host: _eitherOf( 132
127 m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]), 133 List<int> codeUnits = uri.codeUnits;
128 port: _parseIntOrZero(m[_COMPONENT_PORT]), 134 int length = codeUnits.length;
129 path: _emptyIfNull(m[_COMPONENT_PATH]), 135 int index = 0;
130 query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]), 136
131 fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT])); 137 int schemeEndIndex = 0;
138
139 if (length == 0) {
140 return new Uri();
141 }
142
143 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?
144 // Can be scheme.
145 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.
146
147 int codeUnit = codeUnits[index++];
148 if (!isSchemeCharacter(codeUnit)) {
149 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.
150 schemeEndIndex = index;
151 } else {
152 // 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.
153 index--;
154 }
155 break;
156 }
157 }
158 }
159
160 int userInfoEndIndex = -1;
161 int portIndex = -1;
162 int authorityEndIndex = schemeEndIndex;
163 // 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.
164 if (authorityEndIndex == index &&
165 authorityEndIndex + 1 < length &&
166 codeUnits[authorityEndIndex] == '/'.codeUnitAt(0) &&
167 codeUnits[authorityEndIndex + 1] == '/'.codeUnitAt(0)) {
168 // Skip '//'.
169 authorityEndIndex += 2;
170 // It can both be host and userInfo.
171 while (authorityEndIndex < length) {
172 int codeUnit = codeUnits[authorityEndIndex++];
173 if (!isRegName(codeUnit)) {
174 if (codeUnit == '['.codeUnitAt(0)) {
175 // IPv6. Skip to '['.
Søren Gjesse 2014/02/24 09:12:41 '[' -> ']'
Anders Johnsen 2014/02/24 12:44:33 Done.
176 authorityEndIndex = codeUnits.indexOf(']'.codeUnitAt(0),
177 authorityEndIndex) + 1;
178 if (authorityEndIndex == 0) {
179 throw new FormatException("Bad end of IPv6 host");
180 }
181 } else if (portIndex == -1 && codeUnit == ':'.codeUnitAt(0)) {
182 // First time ':'.
183 portIndex = authorityEndIndex;
184 } else if (codeUnit == '@'.codeUnitAt(0) ||
185 codeUnit == ':'.codeUnitAt(0)) {
186 // Second time ':' or first '@'. Must be userInfo.
187 userInfoEndIndex = codeUnits.indexOf('@'.codeUnitAt(0),
188 authorityEndIndex - 1);
189 // Not found. Must be path then.
190 if (userInfoEndIndex == -1) {
191 authorityEndIndex = index;
192 break;
193 }
194 portIndex = -1;
195 authorityEndIndex = userInfoEndIndex + 1;
196 // Now it can only be host:port.
197 while (authorityEndIndex < length) {
198 int codeUnit = codeUnits[authorityEndIndex++];
199 if (!isRegName(codeUnit)) {
200 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.
201 authorityEndIndex = codeUnits.indexOf(']'.codeUnitAt(0),
202 authorityEndIndex) + 1;
203 if (authorityEndIndex == 0) {
204 throw new FormatException("Bad end of IPv6 host");
205 }
206 } else if (codeUnit == ':'.codeUnitAt(0)) {
207 if (portIndex != -1) {
208 throw new FormatException("Double port in host");
209 }
210 portIndex = authorityEndIndex;
211 } else {
212 authorityEndIndex--;
213 break;
214 }
215 }
216 }
217 break;
218 } else {
219 authorityEndIndex--;
220 break;
221 }
222 }
223 }
224 } else {
225 authorityEndIndex = schemeEndIndex;
226 }
227
228 // At path now.
229 int pathEndIndex = authorityEndIndex;
230 while (pathEndIndex < length) {
231 int codeUnit = codeUnits[pathEndIndex++];
232 if (codeUnit == '?'.codeUnitAt(0) ||
233 codeUnit == '#'.codeUnitAt(0)) {
234 pathEndIndex--;
235 break;
236 }
237 }
238
239 // Maybe query.
240 int queryEndIndex = pathEndIndex;
241 if (queryEndIndex < length &&
242 codeUnits[queryEndIndex] == '?'.codeUnitAt(0)) {
243 while (queryEndIndex < length) {
244 int codeUnit = codeUnits[queryEndIndex++];
245 if (codeUnit == '#'.codeUnitAt(0)) {
246 queryEndIndex--;
247 break;
248 }
249 }
250 }
251
252 var scheme = null;
253 if (schemeEndIndex > 0) {
254 scheme = uri.substring(0, schemeEndIndex - 1);
255 }
256
257 var host = "";
258 var userInfo = "";
259 var port = 0;
260 if (schemeEndIndex != authorityEndIndex) {
261 int startIndex = schemeEndIndex + 2;
262 if (userInfoEndIndex > 0) {
263 userInfo = uri.substring(startIndex, userInfoEndIndex);
264 startIndex = userInfoEndIndex + 1;
265 }
266 if (portIndex > 0) {
267 var portStr = uri.substring(portIndex, authorityEndIndex);
268 try {
269 port = int.parse(portStr);
270 } catch (_) {
271 throw new FormatException("Invalid port: '$portStr'");
272 }
273 host = uri.substring(startIndex, portIndex - 1);
274 } else {
275 host = uri.substring(startIndex, authorityEndIndex);
276 }
277 }
278
279 var path = uri.substring(authorityEndIndex, pathEndIndex);
280 var query = "";
281 if (pathEndIndex < queryEndIndex) {
282 query = uri.substring(pathEndIndex + 1, queryEndIndex);
283 }
284 var fragment = "";
285 // If queryEndIndex is not at end (length), there is a fragment.
286 if (queryEndIndex < length) {
287 fragment = uri.substring(queryEndIndex + 1, length);
288 }
289
290 return new Uri(scheme: scheme,
291 userInfo: userInfo,
292 host: host,
293 port: port,
294 path: path,
295 query: query,
296 fragment: fragment);
297 }
132 298
133 /** 299 /**
134 * Creates a new URI from its components. 300 * Creates a new URI from its components.
135 * 301 *
136 * Each component is set through a named argument. Any number of 302 * Each component is set through a named argument. Any number of
137 * components can be provided. The default value for the components 303 * components can be provided. The default value for the components
138 * not provided is the empry string, except for [port] which has a 304 * not provided is the empry string, except for [port] which has a
139 * default value of 0. The [path] and [query] components can be set 305 * default value of 0. The [path] and [query] components can be set
140 * using two different named arguments. 306 * using two different named arguments.
141 * 307 *
(...skipping 570 matching lines...) Expand 10 before | Expand all | Expand 10 after
712 index++; 878 index++;
713 } 879 }
714 } 880 }
715 if (result != null && prevIndex != index) fillResult(); 881 if (result != null && prevIndex != index) fillResult();
716 assert(index == length); 882 assert(index == length);
717 883
718 if (result == null) return component; 884 if (result == null) return component;
719 return result.toString(); 885 return result.toString();
720 } 886 }
721 887
722 static String _emptyIfNull(String val) => val != null ? val : '';
723
724 static int _parseIntOrZero(String val) {
725 if (val != null && val != '') {
726 return int.parse(val);
727 } else {
728 return 0;
729 }
730 }
731
732 static String _eitherOf(String val1, String val2) {
733 if (val1 != null) return val1;
734 if (val2 != null) return val2;
735 return '';
736 }
737
738 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
739 static final RegExp _splitRe = new RegExp(
740 '^'
741 '(?:'
742 '([^:/?#]+)' // scheme - ignore special characters
743 // used by other URL parts such as :,
744 // ?, /, #, and .
745 ':)?'
746 '(?://'
747 '(?:([^/?#]*)@)?' // userInfo
748 '(?:'
749 r'([\w\d\-\u0100-\uffff.%]*)'
750 // host - restrict to letters,
751 // digits, dashes, dots, percent
752 // escapes, and unicode characters.
753 '|'
754 // TODO(ajohnsen): Only allow a max number of parts?
755 r'\[([A-Fa-f0-9:.]*)\])'
756 // IPv6 host - restrict to hex,
757 // dot and colon.
758 '(?::([0-9]+))?' // port
759 ')?'
760 r'([^?#[]+)?' // path
761 r'(?:\?([^#]*))?' // query
762 '(?:#(.*))?' // fragment
763 r'$');
764
765 static const _COMPONENT_SCHEME = 1;
766 static const _COMPONENT_USER_INFO = 2;
767 static const _COMPONENT_HOST = 3;
768 static const _COMPONENT_HOST_IPV6 = 4;
769 static const _COMPONENT_PORT = 5;
770 static const _COMPONENT_PATH = 6;
771 static const _COMPONENT_QUERY_DATA = 7;
772 static const _COMPONENT_FRAGMENT = 8;
773
774 /** 888 /**
775 * Returns whether the URI is absolute. 889 * Returns whether the URI is absolute.
776 */ 890 */
777 bool get isAbsolute => scheme != "" && fragment == ""; 891 bool get isAbsolute => scheme != "" && fragment == "";
778 892
779 String _merge(String base, String reference) { 893 String _merge(String base, String reference) {
780 if (base == "") return "/$reference"; 894 if (base == "") return "/$reference";
781 return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference"; 895 return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference";
782 } 896 }
783 897
(...skipping 834 matching lines...) Expand 10 before | Expand all | Expand 10 after
1618 0x2bff, // 0x30 - 0x3f 1111111111010100 1732 0x2bff, // 0x30 - 0x3f 1111111111010100
1619 // ABCDEFGHIJKLMNO 1733 // ABCDEFGHIJKLMNO
1620 0xfffe, // 0x40 - 0x4f 0111111111111111 1734 0xfffe, // 0x40 - 0x4f 0111111111111111
1621 // PQRSTUVWXYZ _ 1735 // PQRSTUVWXYZ _
1622 0x87ff, // 0x50 - 0x5f 1111111111100001 1736 0x87ff, // 0x50 - 0x5f 1111111111100001
1623 // abcdefghijklmno 1737 // abcdefghijklmno
1624 0xfffe, // 0x60 - 0x6f 0111111111111111 1738 0xfffe, // 0x60 - 0x6f 0111111111111111
1625 // pqrstuvwxyz ~ 1739 // pqrstuvwxyz ~
1626 0x47ff]; // 0x70 - 0x7f 1111111111100010 1740 0x47ff]; // 0x70 - 0x7f 1111111111100010
1627 1741
1742 // Characters allowed in the reg-name as of RFC 3986.
1743 // RFC 3986 Apendix A
1744 // reg-name = *( unreserved / pct-encoded / sub-delims )
1745 static const _regNameTable = const [
1746 // LSB MSB
1747 // | |
1748 0x0000, // 0x00 - 0x0f 0000000000000000
1749 0x0000, // 0x10 - 0x1f 0000000000000000
1750 // ! $%&'()*+,-.
1751 0x7ff2, // 0x20 - 0x2f 0100111111111110
1752 // 0123456789 ; =
1753 0x2bff, // 0x30 - 0x3f 1111111111010100
1754 // ABCDEFGHIJKLMNO
1755 0xfffe, // 0x40 - 0x4f 0111111111111111
1756 // PQRSTUVWXYZ _
1757 0x87ff, // 0x50 - 0x5f 1111111111100001
1758 // abcdefghijklmno
1759 0xfffe, // 0x60 - 0x6f 0111111111111111
1760 // pqrstuvwxyz ~
1761 0x47ff]; // 0x70 - 0x7f 1111111111100010
1762
1628 // Characters allowed in the path as of RFC 3986. 1763 // Characters allowed in the path as of RFC 3986.
1629 // RFC 3986 section 3.3. 1764 // RFC 3986 section 3.3.
1630 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 1765 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1631 static const _pathCharTable = const [ 1766 static const _pathCharTable = const [
1632 // LSB MSB 1767 // LSB MSB
1633 // | | 1768 // | |
1634 0x0000, // 0x00 - 0x0f 0000000000000000 1769 0x0000, // 0x00 - 0x0f 0000000000000000
1635 0x0000, // 0x10 - 0x1f 0000000000000000 1770 0x0000, // 0x10 - 0x1f 0000000000000000
1636 // ! $ &'()*+,-. 1771 // ! $ &'()*+,-.
1637 0x7fd2, // 0x20 - 0x2f 0100101111111110 1772 0x7fd2, // 0x20 - 0x2f 0100101111111110
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
1690 void clear() { 1825 void clear() {
1691 throw new UnsupportedError("Cannot modify an unmodifiable map"); 1826 throw new UnsupportedError("Cannot modify an unmodifiable map");
1692 } 1827 }
1693 void forEach(void f(K key, V value)) => _map.forEach(f); 1828 void forEach(void f(K key, V value)) => _map.forEach(f);
1694 Iterable<K> get keys => _map.keys; 1829 Iterable<K> get keys => _map.keys;
1695 Iterable<V> get values => _map.values; 1830 Iterable<V> get values => _map.values;
1696 int get length => _map.length; 1831 int get length => _map.length;
1697 bool get isEmpty => _map.isEmpty; 1832 bool get isEmpty => _map.isEmpty;
1698 bool get isNotEmpty => _map.isNotEmpty; 1833 bool get isNotEmpty => _map.isNotEmpty;
1699 } 1834 }
OLDNEW
« 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