Revert "Add fast-mode Uri class."

This reverts commit 00090a0c7237b636a73f048e53966879e74ec55a.

Revert "Add fast-mode Uri class."

This reverts commit 323ca7e410765f98a88d078c8c8c388fd972eba9.

Revert "Fix regression for the one case where we deliberately don't follow the RFC."

This reverts commit b39e048c4b74471ad59fa2b62193e4b06055a9df.

Revert "Cache hashCode in Uri implementations to improve performance when used as, e.g., Map key."

This reverts commit a11ad27723b49939307c69d185d7ad5d783d4f5a.

BUG=https://github.com/dart-lang/sdk/issues/26917
TBR=keertip@google.com

Review URL: https://codereview.chromium.org/2167663002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23a3e2a..8f44d60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,6 @@
 
 ### Core library changes
 
-* `dart:core`
-  * Improved performance when parsing some common URIs.
-  * Fixed bug in `Uri.resolve` (SDK issue [26804](https://github.com/dart-lang/sdk/issues/26804)).
 * `dart:io`
   * Adds file locking modes `FileLock.BLOCKING_SHARED` and
     `FileLock.BLOCKING_EXCLUSIVE`.
diff --git a/pkg/analyzer/lib/src/util/fast_uri.dart b/pkg/analyzer/lib/src/util/fast_uri.dart
index 0df5ce05..8af548c 100644
--- a/pkg/analyzer/lib/src/util/fast_uri.dart
+++ b/pkg/analyzer/lib/src/util/fast_uri.dart
@@ -62,7 +62,23 @@
   bool get hasFragment => false;
 
   @override
-  int get hashCode => _text.hashCode;
+  int get hashCode {
+    // This code is copied from the standard Uri implementation.
+    // It is important that Uri and FastUri generate compatible hashCodes
+    // because Uri and FastUri may be used as keys in the same map.
+    int combine(part, current) {
+      // The sum is truncated to 30 bits to make sure it fits into a Smi.
+      return (current * 31 + part.hashCode) & 0x3FFFFFFF;
+    }
+    return _hashCode ??= combine(
+        scheme,
+        combine(
+            userInfo,
+            combine(
+                host,
+                combine(port,
+                    combine(path, combine(query, combine(fragment, 1)))))));
+  }
 
   @override
   bool get hasPort => false;
diff --git a/runtime/lib/uri_patch.dart b/runtime/lib/uri_patch.dart
index 80024c2..2664b9b 100644
--- a/runtime/lib/uri_patch.dart
+++ b/runtime/lib/uri_patch.dart
@@ -16,16 +16,14 @@
 _UriBaseClosure _uriBaseClosure = _unsupportedUriBase;
 
 patch class Uri {
-  /* patch */ static Uri get base => _uriBaseClosure();
-}
-
-patch class _Uri {
   static final bool _isWindowsCached = _isWindowsPlatform;
 
-  static bool get _isWindowsPlatform native "Uri_isWindowsPlatform";
-
   /* patch */ static bool get _isWindows => _isWindowsCached;
 
+  /* patch */ static Uri get base => _uriBaseClosure();
+
+  static bool get _isWindowsPlatform native "Uri_isWindowsPlatform";
+
   /* patch */ static String _uriEncode(List<int> canonicalTable,
                                        String text,
                                        Encoding encoding,
diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
index 129ce01..9004574 100644
--- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
@@ -587,17 +587,15 @@
 @patch
 class Uri {
   @patch
+  static bool get _isWindows => false;
+
+  @patch
   static Uri get base {
     String uri = Primitives.currentUri();
     if (uri != null) return Uri.parse(uri);
     throw new UnsupportedError("'Uri.base' is not supported");
   }
-}
 
-@patch
-class _Uri {
-  @patch
-  static bool get _isWindows => false;
 
   // Matches a String that _uriEncodes to itself regardless of the kind of
   // component.  This corresponds to [_unreservedTable], i.e. characters that
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
index b43b3ad..5718ca8 100644
--- a/sdk/lib/core/uri.dart
+++ b/sdk/lib/core/uri.dart
@@ -4,24 +4,6 @@
 
 part of dart.core;
 
-// Frequently used character codes.
-const int _SPACE = 0x20;
-const int _PERCENT = 0x25;
-const int _PLUS = 0x2B;
-const int _DOT = 0x2E;
-const int _SLASH = 0x2F;
-const int _COLON = 0x3A;
-const int _UPPER_CASE_A = 0x41;
-const int _UPPER_CASE_Z = 0x5A;
-const int _LEFT_BRACKET = 0x5B;
-const int _BACKSLASH = 0x5C;
-const int _RIGHT_BRACKET = 0x5D;
-const int _LOWER_CASE_A = 0x61;
-const int _LOWER_CASE_F = 0x66;
-const int _LOWER_CASE_Z = 0x7A;
-
-const String _hexDigits = "0123456789ABCDEF";
-
 /**
  * A parsed URI, such as a URL.
  *
@@ -33,17 +15,77 @@
  * [uris]: https://www.dartlang.org/docs/dart-up-and-running/ch03.html#uris
  * [libtour]: https://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html
  */
-abstract class Uri {
+class Uri {
   /**
-   * Returns the natural base URI for the current platform.
+   * The scheme component of the URI.
    *
-   * When running in a browser this is the current URL of the current page
-   * (from `window.location.href`).
+   * Returns the empty string if there is no scheme component.
    *
-   * When not running in a browser this is the file URI referencing
-   * the current working directory.
+   * A URI scheme is case insensitive.
+   * The returned scheme is canonicalized to lowercase letters.
    */
-  external static Uri get base;
+  // We represent the missing scheme as an empty string.
+  // A valid scheme cannot be empty.
+  final String scheme;
+
+  /**
+   * The user-info part of the authority.
+   *
+   * Does not distinguish between an empty user-info and an absent one.
+   * The value is always non-null.
+   * Is considered absent if [_host] is `null`.
+   */
+  final String _userInfo;
+
+  /**
+   * The host name of the URI.
+   *
+   * Set to `null` if there is no authority in the URI.
+   * The host name is the only mandatory part of an authority, so we use
+   * it to mark whether an authority part was present or not.
+   */
+  final String _host;
+
+  /**
+   * The port number part of the authority.
+   *
+   * The port. Set to null if there is no port. Normalized to null if
+   * the port is the default port for the scheme.
+   */
+  int _port;
+
+  /**
+   * The path of the URI.
+   *
+   * Always non-null.
+   */
+  String _path;
+
+  // The query content, or null if there is no query.
+  final String _query;
+
+  // The fragment content, or null if there is no fragment.
+  final String _fragment;
+
+  /**
+   * Cache the computed return value of [pathSegements].
+   */
+  List<String> _pathSegments;
+
+  /**
+   * Cache the computed return value of [queryParameters].
+   */
+  Map<String, String> _queryParameters;
+  Map<String, List<String>> _queryParameterLists;
+
+  /// Internal non-verifying constructor. Only call with validated arguments.
+  Uri._internal(this.scheme,
+                this._userInfo,
+                this._host,
+                this._port,
+                this._path,
+                this._query,
+                this._fragment);
 
   /**
    * Creates a new URI from its components.
@@ -116,15 +158,39 @@
    * general delimiters, are escaped if necessary.
    * If `fragment` is omitted or `null`, the URI has no fragment part.
    */
-  factory Uri({String scheme,
-               String userInfo,
+  factory Uri({String scheme : "",
+               String userInfo : "",
                String host,
                int port,
                String path,
                Iterable<String> pathSegments,
                String query,
                Map<String, dynamic/*String|Iterable<String>*/> queryParameters,
-               String fragment}) = _Uri;
+               String fragment}) {
+    scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme));
+    userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo));
+    host = _makeHost(host, 0, _stringOrNullLength(host), false);
+    // Special case this constructor for backwards compatibility.
+    if (query == "") query = null;
+    query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
+    fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment));
+    port = _makePort(port, scheme);
+    bool isFile = (scheme == "file");
+    if (host == null &&
+        (userInfo.isNotEmpty || port != null || isFile)) {
+      host = "";
+    }
+    bool hasAuthority = (host != null);
+    path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
+                     scheme, hasAuthority);
+    if (scheme.isEmpty && host == null && !path.startsWith('/')) {
+      path = _normalizeRelativePath(path);
+    } else {
+      path = _removeDotSegments(path);
+    }
+    return new Uri._internal(scheme, userInfo, host, port,
+                             path, query, fragment);
+  }
 
   /**
    * Creates a new `http` URI from authority, path and query.
@@ -161,7 +227,9 @@
    */
   factory Uri.http(String authority,
                    String unencodedPath,
-                   [Map<String, String> queryParameters]) = _Uri.http;
+                   [Map<String, String> queryParameters]) {
+    return _makeHttpUri("http", authority, unencodedPath, queryParameters);
+  }
 
   /**
    * Creates a new `https` URI from authority, path and query.
@@ -171,7 +239,424 @@
    */
   factory Uri.https(String authority,
                     String unencodedPath,
-                    [Map<String, String> queryParameters]) = _Uri.https;
+                    [Map<String, String> queryParameters]) {
+    return _makeHttpUri("https", authority, unencodedPath, queryParameters);
+  }
+
+  /**
+   * Returns the authority component.
+   *
+   * The authority is formatted from the [userInfo], [host] and [port]
+   * parts.
+   *
+   * Returns the empty string if there is no authority component.
+   */
+  String get authority {
+    if (!hasAuthority) return "";
+    var sb = new StringBuffer();
+    _writeAuthority(sb);
+    return sb.toString();
+  }
+
+  /**
+   * Returns the user info part of the authority component.
+   *
+   * Returns the empty string if there is no user info in the
+   * authority component.
+   */
+  String get userInfo => _userInfo;
+
+  /**
+   * Returns the host part of the authority component.
+   *
+   * Returns the empty string if there is no authority component and
+   * hence no host.
+   *
+   * If the host is an IP version 6 address, the surrounding `[` and `]` is
+   * removed.
+   *
+   * The host string is case-insensitive.
+   * The returned host name is canonicalized to lower-case
+   * with upper-case percent-escapes.
+   */
+  String get host {
+    if (_host == null) return "";
+    if (_host.startsWith('[')) {
+      return _host.substring(1, _host.length - 1);
+    }
+    return _host;
+  }
+
+  /**
+   * Returns the port part of the authority component.
+   *
+   * Returns the defualt port if there is no port number in the authority
+   * component. That's 80 for http, 443 for https, and 0 for everything else.
+   */
+  int get port {
+    if (_port == null) return _defaultPort(scheme);
+    return _port;
+  }
+
+  // The default port for the scheme of this Uri..
+  static int _defaultPort(String scheme) {
+    if (scheme == "http") return 80;
+    if (scheme == "https") return 443;
+    return 0;
+  }
+
+  /**
+   * Returns the path component.
+   *
+   * The returned path is encoded. To get direct access to the decoded
+   * path use [pathSegments].
+   *
+   * Returns the empty string if there is no path component.
+   */
+  String get path => _path;
+
+  /**
+   * Returns the query component. The returned query is encoded. To get
+   * direct access to the decoded query use [queryParameters].
+   *
+   * Returns the empty string if there is no query component.
+   */
+  String get query => (_query == null) ? "" : _query;
+
+  /**
+   * Returns the fragment identifier component.
+   *
+   * Returns the empty string if there is no fragment identifier
+   * component.
+   */
+  String get fragment => (_fragment == null) ? "" : _fragment;
+
+  /**
+   * Creates a new `Uri` object by parsing a URI string.
+   *
+   * If [start] and [end] are provided, only the substring from `start`
+   * to `end` is parsed as a URI.
+   *
+   * If the string is not valid as a URI or URI reference,
+   * a [FormatException] is thrown.
+   */
+  static Uri parse(String uri, [int start = 0, int end]) {
+    // This parsing will not validate percent-encoding, IPv6, etc.
+    // When done splitting into parts, it will call, e.g., [_makeFragment]
+    // to do the final parsing.
+    //
+    // 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 / "/" / "?" )
+    const int EOI = -1;
+
+    String scheme = "";
+    String userinfo = "";
+    String host = null;
+    int port = null;
+    String path = null;
+    String query = null;
+    String fragment = null;
+    if (end == null) end = uri.length;
+
+    int index = start;
+    int pathStart = start;
+    // End of input-marker.
+    int char = EOI;
+
+    void parseAuth() {
+      if (index == end) {
+        char = EOI;
+        return;
+      }
+      int authStart = index;
+      int lastColon = -1;
+      int lastAt = -1;
+      char = uri.codeUnitAt(index);
+      while (index < end) {
+        char = uri.codeUnitAt(index);
+        if (char == _SLASH || char == _QUESTION || char == _NUMBER_SIGN) {
+          break;
+        }
+        if (char == _AT_SIGN) {
+          lastAt = index;
+          lastColon = -1;
+        } else if (char == _COLON) {
+          lastColon = index;
+        } else if (char == _LEFT_BRACKET) {
+          lastColon = -1;
+          int endBracket = uri.indexOf(']', index + 1);
+          if (endBracket == -1) {
+            index = end;
+            char = EOI;
+            break;
+          } else {
+            index = endBracket;
+          }
+        }
+        index++;
+        char = EOI;
+      }
+      int hostStart = authStart;
+      int hostEnd = index;
+      if (lastAt >= 0) {
+        userinfo = _makeUserInfo(uri, authStart, lastAt);
+        hostStart = lastAt + 1;
+      }
+      if (lastColon >= 0) {
+        int portNumber;
+        if (lastColon + 1 < index) {
+          portNumber = 0;
+          for (int i = lastColon + 1; i < index; i++) {
+            int digit = uri.codeUnitAt(i);
+            if (_ZERO > digit || _NINE < digit) {
+              _fail(uri, i, "Invalid port number");
+            }
+            portNumber = portNumber * 10 + (digit - _ZERO);
+          }
+        }
+        port = _makePort(portNumber, scheme);
+        hostEnd = lastColon;
+      }
+      host = _makeHost(uri, hostStart, hostEnd, true);
+      if (index < end) {
+        char = uri.codeUnitAt(index);
+      }
+    }
+
+    // When reaching path parsing, the current character is known to not
+    // be part of the path.
+    const int NOT_IN_PATH = 0;
+    // When reaching path parsing, the current character is part
+    // of the a non-empty path.
+    const int IN_PATH = 1;
+    // When reaching authority parsing, authority is possible.
+    // This is only true at start or right after scheme.
+    const int ALLOW_AUTH = 2;
+
+    // Current state.
+    // Initialized to the default value that is used when exiting the
+    // scheme loop by reaching the end of input.
+    // All other breaks set their own state.
+    int state = NOT_IN_PATH;
+    int i = index;  // Temporary alias for index to avoid bug 19550 in dart2js.
+    while (i < end) {
+      char = uri.codeUnitAt(i);
+      if (char == _QUESTION || char == _NUMBER_SIGN) {
+        state = NOT_IN_PATH;
+        break;
+      }
+      if (char == _SLASH) {
+        state = (i == start) ? ALLOW_AUTH : IN_PATH;
+        break;
+      }
+      if (char == _COLON) {
+        if (i == start) _fail(uri, start, "Invalid empty scheme");
+        scheme = _makeScheme(uri, start, i);
+        i++;
+        if (scheme == "data") {
+          // This generates a URI that is (potentially) not path normalized.
+          // Applying part normalization to a non-hierarchial URI isn't
+          // meaningful.
+          return UriData._parse(uri, i, null).uri;
+        }
+        pathStart = i;
+        if (i == end) {
+          char = EOI;
+          state = NOT_IN_PATH;
+        } else {
+          char = uri.codeUnitAt(i);
+          if (char == _QUESTION || char == _NUMBER_SIGN) {
+            state = NOT_IN_PATH;
+          } else if (char == _SLASH) {
+            state = ALLOW_AUTH;
+          } else {
+            state = IN_PATH;
+          }
+        }
+        break;
+      }
+      i++;
+      char = EOI;
+    }
+    index = i;  // Remove alias when bug is fixed.
+
+    if (state == ALLOW_AUTH) {
+      assert(char == _SLASH);
+      // Have seen one slash either at start or right after scheme.
+      // If two slashes, it's an authority, otherwise it's just the path.
+      index++;
+      if (index == end) {
+        char = EOI;
+        state = NOT_IN_PATH;
+      } else {
+        char = uri.codeUnitAt(index);
+        if (char == _SLASH) {
+          index++;
+          parseAuth();
+          pathStart = index;
+        }
+        if (char == _QUESTION || char == _NUMBER_SIGN || char == EOI) {
+          state = NOT_IN_PATH;
+        } else {
+          state = IN_PATH;
+        }
+      }
+    }
+
+    assert(state == IN_PATH || state == NOT_IN_PATH);
+    if (state == IN_PATH) {
+      // Characters from pathStart to index (inclusive) are known
+      // to be part of the path.
+      while (++index < end) {
+        char = uri.codeUnitAt(index);
+        if (char == _QUESTION || char == _NUMBER_SIGN) {
+          break;
+        }
+        char = EOI;
+      }
+      state = NOT_IN_PATH;
+    }
+
+    assert(state == NOT_IN_PATH);
+    bool hasAuthority = (host != null);
+    path = _makePath(uri, pathStart, index, null, scheme, hasAuthority);
+
+    if (char == _QUESTION) {
+      int numberSignIndex = -1;
+      for (int i = index + 1; i < end; i++) {
+        if (uri.codeUnitAt(i) == _NUMBER_SIGN) {
+          numberSignIndex = i;
+          break;
+        }
+      }
+      if (numberSignIndex < 0) {
+        query = _makeQuery(uri, index + 1, end, null);
+      } else {
+        query = _makeQuery(uri, index + 1, numberSignIndex, null);
+        fragment = _makeFragment(uri, numberSignIndex + 1, end);
+      }
+    } else if (char == _NUMBER_SIGN) {
+      fragment = _makeFragment(uri, index + 1, end);
+    }
+    return new Uri._internal(scheme,
+                             userinfo,
+                             host,
+                             port,
+                             path,
+                             query,
+                             fragment);
+  }
+
+  // Report a parse failure.
+  static void _fail(String uri, int index, String message) {
+    throw new FormatException(message, uri, index);
+  }
+
+  static Uri _makeHttpUri(String scheme,
+                          String authority,
+                          String unencodedPath,
+                          Map<String, String> queryParameters) {
+    var userInfo = "";
+    var host = null;
+    var port = null;
+
+    if (authority != null && authority.isNotEmpty) {
+      var hostStart = 0;
+      // Split off the user info.
+      bool hasUserInfo = false;
+      for (int i = 0; i < authority.length; i++) {
+        if (authority.codeUnitAt(i) == _AT_SIGN) {
+          hasUserInfo = true;
+          userInfo = authority.substring(0, i);
+          hostStart = i + 1;
+          break;
+        }
+      }
+      var hostEnd = hostStart;
+      if (hostStart < authority.length &&
+          authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
+        // IPv6 host.
+        for (; hostEnd < authority.length; hostEnd++) {
+          if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
+        }
+        if (hostEnd == authority.length) {
+          throw new FormatException("Invalid IPv6 host entry.",
+                                    authority, hostStart);
+        }
+        parseIPv6Address(authority, hostStart + 1, hostEnd);
+        hostEnd++;  // Skip the closing bracket.
+        if (hostEnd != authority.length &&
+            authority.codeUnitAt(hostEnd) != _COLON) {
+          throw new FormatException("Invalid end of authority",
+                                    authority, hostEnd);
+        }
+      }
+      // Split host and port.
+      bool hasPort = false;
+      for (; hostEnd < authority.length; hostEnd++) {
+        if (authority.codeUnitAt(hostEnd) == _COLON) {
+          var portString = authority.substring(hostEnd + 1);
+          // We allow the empty port - falling back to initial value.
+          if (portString.isNotEmpty) port = int.parse(portString);
+          break;
+        }
+      }
+      host = authority.substring(hostStart, hostEnd);
+    }
+    return new Uri(scheme: scheme,
+                   userInfo: userInfo,
+                   host: host,
+                   port: port,
+                   pathSegments: unencodedPath.split("/"),
+                   queryParameters: queryParameters);
+  }
 
   /**
    * Creates a new file URI from an absolute or relative file path.
@@ -254,7 +739,11 @@
    *
    * If the path passed is not a legal file path [ArgumentError] is thrown.
    */
-  factory Uri.file(String path, {bool windows}) = _Uri.file;
+  factory Uri.file(String path, {bool windows}) {
+    windows = (windows == null) ? Uri._isWindows : windows;
+    return windows ? _makeWindowsFileUrl(path, false)
+                   : _makeFileUri(path, false);
+  }
 
   /**
    * Like [Uri.file] except that a non-empty URI path ends in a slash.
@@ -263,7 +752,11 @@
    * then a slash is added to the returned URI's path.
    * In all other cases, the result is the same as returned by `Uri.file`.
    */
-  factory Uri.directory(String path, {bool windows}) = _Uri.directory;
+  factory Uri.directory(String path, {bool windows}) {
+    windows = (windows == null) ? Uri._isWindows : windows;
+    return windows ? _makeWindowsFileUrl(path, true)
+                   : _makeFileUri(path, true);
+  }
 
   /**
    * Creates a `data:` URI containing the [content] string.
@@ -334,1280 +827,16 @@
   }
 
   /**
-   * The scheme component of the URI.
+   * Returns the natural base URI for the current platform.
    *
-   * Returns the empty string if there is no scheme component.
+   * When running in a browser this is the current URL (from
+   * `window.location.href`).
    *
-   * A URI scheme is case insensitive.
-   * The returned scheme is canonicalized to lowercase letters.
+   * When not running in a browser this is the file URI referencing
+   * the current working directory.
    */
-  String get scheme;
-
-  /**
-   * Returns the authority component.
-   *
-   * The authority is formatted from the [userInfo], [host] and [port]
-   * parts.
-   *
-   * Returns the empty string if there is no authority component.
-   */
-  String get authority;
-
-  /**
-   * Returns the user info part of the authority component.
-   *
-   * Returns the empty string if there is no user info in the
-   * authority component.
-   */
-  String get userInfo;
-
-  /**
-   * Returns the host part of the authority component.
-   *
-   * Returns the empty string if there is no authority component and
-   * hence no host.
-   *
-   * If the host is an IP version 6 address, the surrounding `[` and `]` is
-   * removed.
-   *
-   * The host string is case-insensitive.
-   * The returned host name is canonicalized to lower-case
-   * with upper-case percent-escapes.
-   */
-  String get host;
-
-  /**
-   * Returns the port part of the authority component.
-   *
-   * Returns the defualt port if there is no port number in the authority
-   * component. That's 80 for http, 443 for https, and 0 for everything else.
-   */
-  int get port;
-
-  /**
-   * Returns the path component.
-   *
-   * The returned path is encoded. To get direct access to the decoded
-   * path use [pathSegments].
-   *
-   * Returns the empty string if there is no path component.
-   */
-  String get path;
-
-  /**
-   * Returns the query component. The returned query is encoded. To get
-   * direct access to the decoded query use [queryParameters].
-   *
-   * Returns the empty string if there is no query component.
-   */
-  String get query;
-
-  /**
-   * Returns the fragment identifier component.
-   *
-   * Returns the empty string if there is no fragment identifier
-   * component.
-   */
-  String get fragment;
-
-  /**
-   * Returns the URI path split into its segments. Each of the segments in the
-   * returned list have been decoded. If the path is empty the empty list will
-   * be returned. A leading slash `/` does not affect the segments returned.
-   *
-   * The returned list is unmodifiable and will throw [UnsupportedError] on any
-   * calls that would mutate it.
-   */
-  List<String> get pathSegments;
-
-  /**
-   * Returns the URI query split into a map according to the rules
-   * specified for FORM post in the [HTML 4.01 specification section
-   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
-   * Each key and value in the returned map has been decoded.
-   * If there is no query the empty map is returned.
-   *
-   * Keys in the query string that have no value are mapped to the
-   * empty string.
-   * If a key occurs more than once in the query string, it is mapped to
-   * an arbitrary choice of possible value.
-   * The [queryParametersAll] getter can provide a map
-   * that maps keys to all of their values.
-   *
-   * The returned map is unmodifiable.
-   */
-  Map<String, String> get queryParameters;
-
-  /**
-   * Returns the URI query split into a map according to the rules
-   * specified for FORM post in the [HTML 4.01 specification section
-   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
-   * Each key and value in the returned map has been decoded. If there is no
-   * query the empty map is returned.
-   *
-   * Keys are mapped to lists of their values. If a key occurs only once,
-   * its value is a singleton list. If a key occurs with no value, the
-   * empty string is used as the value for that occurrence.
-   *
-   * The returned map and the lists it contains are unmodifiable.
-   */
-  Map<String, List<String>> get queryParametersAll;
-
-  /**
-   * Returns whether the URI is absolute.
-   *
-   * A URI is an absolute URI in the sense of RFC 3986 if it has a scheme
-   * and no fragment.
-   */
-  bool get isAbsolute;
-
-  /**
-   * Returns whether the URI has a [scheme] component.
-   */
-  bool get hasScheme => scheme.isNotEmpty;
-
-  /**
-   * Returns whether the URI has an [authority] component.
-   */
-  bool get hasAuthority;
-
-  /**
-   * Returns whether the URI has an explicit port.
-   *
-   * If the port number is the default port number
-   * (zero for unrecognized schemes, with http (80) and https (443) being
-   * recognized),
-   * then the port is made implicit and omitted from the URI.
-   */
-  bool get hasPort;
-
-  /**
-   * Returns whether the URI has a query part.
-   */
-  bool get hasQuery;
-
-  /**
-   * Returns whether the URI has a fragment part.
-   */
-  bool get hasFragment;
-
-  /**
-   * Returns whether the URI has an empty path.
-   */
-  bool get hasEmptyPath;
-
-  /**
-   * Returns whether the URI has an absolute path (starting with '/').
-   */
-  bool get hasAbsolutePath;
-
-  /**
-   * Returns the origin of the URI in the form scheme://host:port for the
-   * schemes http and https.
-   *
-   * It is an error if the scheme is not "http" or "https".
-   *
-   * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
-   */
-  String get origin;
-
-  /**
-   * Returns the file path from a file URI.
-   *
-   * The returned path has either Windows or non-Windows
-   * semantics.
-   *
-   * For non-Windows semantics the slash ("/") is used to separate
-   * path segments.
-   *
-   * For Windows semantics the backslash ("\") separator is used to
-   * separate path segments.
-   *
-   * If the URI is absolute the path starts with a path separator
-   * unless Windows semantics is used and the first path segment is a
-   * drive letter. When Windows semantics is used a host component in
-   * the uri in interpreted as a file server and a UNC path is
-   * returned.
-   *
-   * The default for whether to use Windows or non-Windows semantics
-   * determined from the platform Dart is running on. When running in
-   * the standalone VM this is detected by the VM based on the
-   * operating system. When running in a browser non-Windows semantics
-   * is always used.
-   *
-   * To override the automatic detection of which semantics to use pass
-   * a value for [windows]. Passing `true` will use Windows
-   * semantics and passing `false` will use non-Windows semantics.
-   *
-   * If the URI ends with a slash (i.e. the last path component is
-   * empty) the returned file path will also end with a slash.
-   *
-   * With Windows semantics URIs starting with a drive letter cannot
-   * be relative to the current drive on the designated drive. That is
-   * for the URI `file:///c:abc` calling `toFilePath` will throw as a
-   * path segment cannot contain colon on Windows.
-   *
-   * Examples using non-Windows semantics (resulting of calling
-   * toFilePath in comment):
-   *
-   *     Uri.parse("xxx/yyy");  // xxx/yyy
-   *     Uri.parse("xxx/yyy/");  // xxx/yyy/
-   *     Uri.parse("file:///xxx/yyy");  // /xxx/yyy
-   *     Uri.parse("file:///xxx/yyy/");  // /xxx/yyy/
-   *     Uri.parse("file:///C:");  // /C:
-   *     Uri.parse("file:///C:a");  // /C:a
-   *
-   * Examples using Windows semantics (resulting URI in comment):
-   *
-   *     Uri.parse("xxx/yyy");  // xxx\yyy
-   *     Uri.parse("xxx/yyy/");  // xxx\yyy\
-   *     Uri.parse("file:///xxx/yyy");  // \xxx\yyy
-   *     Uri.parse("file:///xxx/yyy/");  // \xxx\yyy/
-   *     Uri.parse("file:///C:/xxx/yyy");  // C:\xxx\yyy
-   *     Uri.parse("file:C:xxx/yyy");  // Throws as a path segment
-   *                                   // cannot contain colon on Windows.
-   *     Uri.parse("file://server/share/file");  // \\server\share\file
-   *
-   * If the URI is not a file URI calling this throws
-   * [UnsupportedError].
-   *
-   * If the URI cannot be converted to a file path calling this throws
-   * [UnsupportedError].
-   */
-  // TODO(lrn): Deprecate and move functionality to File class or similar.
-  // The core libraries should not worry about the platform.
-  String toFilePath({bool windows});
-
-  /**
-   * Access the structure of a `data:` URI.
-   *
-   * Returns a [UriData] object for `data:` URIs and `null` for all other
-   * URIs.
-   * The [UriData] object can be used to access the media type and data
-   * of a `data:` URI.
-   */
-  UriData get data;
-
-  /// Returns a hash code computed as `toString().hashCode`.
-  ///
-  /// This guarantees that URIs with the same normalized
-  int get hashCode;
-
-  /// A URI is equal to another URI with the same normalized representation.
-  bool operator==(Object other);
-
-  /// Returns the normalized string representation of the URI.
-  String toString();
-
-  /**
-   * Returns a new `Uri` based on this one, but with some parts replaced.
-   *
-   * This method takes the same parameters as the [new Uri] constructor,
-   * and they have the same meaning.
-   *
-   * At most one of [path] and [pathSegments] must be provided.
-   * Likewise, at most one of [query] and [queryParameters] must be provided.
-   *
-   * Each part that is not provided will default to the corresponding
-   * value from this `Uri` instead.
-   *
-   * This method is different from [Uri.resolve] which overrides in a
-   * hierarchial manner,
-   * and can instead replace each part of a `Uri` individually.
-   *
-   * Example:
-   *
-   *     Uri uri1 = Uri.parse("a://b@c:4/d/e?f#g");
-   *     Uri uri2 = uri1.replace(scheme: "A", path: "D/E/E", fragment: "G");
-   *     print(uri2);  // prints "A://b@c:4/D/E/E/?f#G"
-   *
-   * This method acts similarly to using the `new Uri` constructor with
-   * some of the arguments taken from this `Uri` . Example:
-   *
-   *     Uri uri3 = new Uri(
-   *         scheme: "A",
-   *         userInfo: uri1.userInfo,
-   *         host: uri1.host,
-   *         port: uri1.port,
-   *         path: "D/E/E",
-   *         query: uri1.query,
-   *         fragment: "G");
-   *     print(uri3);  // prints "A://b@c:4/D/E/E/?f#G"
-   *     print(uri2 == uri3);  // prints true.
-   *
-   * Using this method can be seen as a shorthand for the `Uri` constructor
-   * call above, but may also be slightly faster because the parts taken
-   * from this `Uri` need not be checked for validity again.
-   */
-  Uri replace({String scheme,
-               String userInfo,
-               String host,
-               int port,
-               String path,
-               Iterable<String> pathSegments,
-               String query,
-               Map<String, dynamic/*String|Iterable<String>*/> queryParameters,
-               String fragment});
-
-  /**
-   * Returns a `Uri` that differs from this only in not having a fragment.
-   *
-   * If this `Uri` does not have a fragment, it is itself returned.
-   */
-  Uri removeFragment();
-
-  /**
-   * Resolve [reference] as an URI relative to `this`.
-   *
-   * First turn [reference] into a URI using [Uri.parse]. Then resolve the
-   * resulting URI relative to `this`.
-   *
-   * Returns the resolved URI.
-   *
-   * See [resolveUri] for details.
-   */
-  Uri resolve(String reference);
-
-  /**
-   * Resolve [reference] as an URI relative to `this`.
-   *
-   * Returns the resolved URI.
-   *
-   * The algorithm "Transform Reference" for resolving a reference is described
-   * in [RFC-3986 Section 5](http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123").
-   *
-   * Updated to handle the case where the base URI is just a relative path -
-   * that is: when it has no scheme or authority and the path does not start
-   * with a slash.
-   * In that case, the paths are combined without removing leading "..", and
-   * an empty path is not converted to "/".
-   */
-  Uri resolveUri(Uri reference);
-
-  /**
-   * Returns a URI where the path has been normalized.
-   *
-   * A normalized path does not contain `.` segments or non-leading `..`
-   * segments.
-   * Only a relative path with no scheme or authority may contain
-   * leading `..` segments,
-   * a path that starts with `/` will also drop any leading `..` segments.
-   *
-   * This uses the same normalization strategy as `new Uri().resolve(this)`.
-   *
-   * Does not change any part of the URI except the path.
-   *
-   * The default implementation of `Uri` always normalizes paths, so calling
-   * this function has no effect.
-   */
-  Uri normalizePath();
-
-  /**
-   * Creates a new `Uri` object by parsing a URI string.
-   *
-   * If [start] and [end] are provided, only the substring from `start`
-   * to `end` is parsed as a URI.
-   *
-   * If the string is not valid as a URI or URI reference,
-   * a [FormatException] is thrown.
-   */
-  static Uri parse(String uri, [int start = 0, int end]) {
-    // This parsing will not validate percent-encoding, IPv6, etc.
-    // When done splitting into parts, it will call, e.g., [_makeFragment]
-    // to do the final parsing.
-    //
-    // 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 / "/" / "?" )
-    end ??= uri.length;
-
-    // Special case data:URIs. Ignore case when testing.
-    if (end >= start + 5) {
-      int dataDelta = _startsWithData(uri, start);
-      if (dataDelta == 0) {
-        // The case is right.
-        if (start > 0 || end < uri.length) uri = uri.substring(start, end);
-        return UriData._parse(uri, 5, null).uri;
-      } else if (dataDelta == 0x20) {
-        return UriData._parse(uri.substring(start + 5, end), 0, null).uri;
-      }
-      // Otherwise the URI doesn't start with "data:" or any case variant of it.
-    }
-
-    // The following index-normalization belongs with the scanning, but is
-    // easier to do here because we already have extracted variables from the
-    // indices list.
-    var indices = new List<int>(8);//new List<int>.filled(8, start - 1);
-
-    // Set default values for each position.
-    // The value will either be correct in some cases where it isn't set
-    // by the scanner, or it is clearly recognizable as an unset value.
-    indices
-      ..[0] = 0
-      ..[_schemeEndIndex] = start - 1
-      ..[_hostStartIndex] = start - 1
-      ..[_notSimpleIndex] = start - 1
-      ..[_portStartIndex] = start
-      ..[_pathStartIndex] = start
-      ..[_queryStartIndex] = end
-      ..[_fragmentStartIndex] = end;
-    var state = _scan(uri, start, end, _uriStart, indices);
-    // Some states that should be non-simple, but the URI ended early.
-    // Paths that end at a ".." must be normalized to end in "../".
-    if (state >= _nonSimpleEndStates) {
-      indices[_notSimpleIndex] = end;
-    }
-    int schemeEnd = indices[_schemeEndIndex];
-    if (schemeEnd >= start) {
-      // Rescan the scheme part now that we know it's not a path.
-      state = _scan(uri, start, schemeEnd, _schemeStart, indices);
-      if (state == _schemeStart) {
-        // Empty scheme.
-        indices[_notSimpleIndex] = schemeEnd;
-      }
-    }
-    // The returned positions are limited by the scanners ability to write only
-    // one position per character, and only the current position.
-    // Scanning from left to right, we only know whether something is a scheme
-    // or a path when we see a `:` or `/`, and likewise we only know if the first
-    // `/` is part of the path or is leading an authority component when we see
-    // the next character.
-
-    int hostStart     = indices[_hostStartIndex] + 1;
-    int portStart     = indices[_portStartIndex];
-    int pathStart     = indices[_pathStartIndex];
-    int queryStart    = indices[_queryStartIndex];
-    int fragmentStart = indices[_fragmentStartIndex];
-
-    // We may discover scheme while handling special cases.
-    String scheme;
-
-    // Derive some positions that weren't set to normalize the indices.
-    // If pathStart isn't set (it's before scheme end or host start), then
-    // the path is empty.
-    if (fragmentStart < queryStart) queryStart = fragmentStart;
-    if (pathStart < hostStart || pathStart <= schemeEnd) {
-      pathStart = queryStart;
-    }
-    // If there is an authority with no port, set the port position
-    // to be at the end of the authority (equal to pathStart).
-    // This also handles a ":" in a user-info component incorrectly setting
-    // the port start position.
-    if (portStart < hostStart) portStart = pathStart;
-
-    assert(hostStart == start || schemeEnd <= hostStart);
-    assert(hostStart <= portStart);
-    assert(schemeEnd <= pathStart);
-    assert(portStart <= pathStart);
-    assert(pathStart <= queryStart);
-    assert(queryStart <= fragmentStart);
-
-    bool isSimple = indices[_notSimpleIndex] < start;
-
-    if (isSimple) {
-      // Check/do normalizations that weren't detected by the scanner.
-      // This includes removal of empty port or userInfo,
-      // or scheme specific port and path normalizations.
-      if (hostStart > schemeEnd + 3) {
-        // Always be non-simple if URI contains user-info.
-        // The scanner doesn't set the not-simple position in this case because
-        // it's setting the host-start position instead.
-        isSimple = false;
-      } else if (portStart > start && portStart + 1 == pathStart) {
-        // If the port is empty, it should be omitted.
-        // Pathological case, don't bother correcting it.
-        isSimple = false;
-      } else if (queryStart < end &&
-                 (queryStart == pathStart + 2 &&
-                  uri.startsWith("..", pathStart)) ||
-                 (queryStart > pathStart + 2 &&
-                  uri.startsWith("/..", queryStart - 3))) {
-        // The path ends in a ".." segment. This should be normalized to "../".
-        // We didn't detect this while scanning because a query or fragment was
-        // detected at the same time (which is why we only need to check this
-        // if there is something after the path).
-        isSimple = false;
-      } else {
-        // There are a few scheme-based normalizations that
-        // the scanner couldn't check.
-        // That means that the input is very close to simple, so just do
-        // the normalizations.
-        if (schemeEnd == start + 4) {
-          // Do scheme based normalizations for file, http.
-          if (uri.startsWith("file", start)) {
-            scheme = "file";
-            if (hostStart <= start) {
-              // File URIs should have an authority.
-              // Paths after an authority should be absolute.
-              String schemeAuth = "file://";
-              int delta = 2;
-              if (!uri.startsWith("/", pathStart)) {
-                schemeAuth = "file:///";
-                delta = 3;
-              }
-              uri = schemeAuth + uri.substring(pathStart, end);
-              schemeEnd -= start;
-              hostStart = 7;
-              portStart = 7;
-              pathStart = 7;
-              queryStart += delta - start;
-              fragmentStart += delta - start;
-              start = 0;
-              end = uri.length;
-            } else if (pathStart == queryStart) {
-              // Uri has authority and empty path. Add "/" as path.
-              if (start == 0 && end == uri.length) {
-                uri = uri.replaceRange(pathStart, queryStart, "/");
-                queryStart += 1;
-                fragmentStart += 1;
-                end += 1;
-              } else {
-                uri = "${uri.substring(start, pathStart)}/"
-                      "${uri.substring(queryStart, end)}";
-                schemeEnd -= start;
-                hostStart -= start;
-                portStart -= start;
-                pathStart -= start;
-                queryStart += 1 - start;
-                fragmentStart += 1 - start;
-                start = 0;
-                end = uri.length;
-              }
-            }
-          } else if (uri.startsWith("http", start)) {
-            scheme = "http";
-            // HTTP URIs should not have an explicit port of 80.
-            if (portStart > start && portStart + 3 == pathStart &&
-                uri.startsWith("80", portStart + 1)) {
-              if (start == 0 && end == uri.length) {
-                uri = uri.replaceRange(portStart, pathStart, "");
-                pathStart -= 3;
-                queryStart -= 3;
-                fragmentStart -= 3;
-                end -= 3;
-              } else {
-                uri = uri.substring(start, portStart) +
-                      uri.substring(pathStart, end);
-                schemeEnd -= start;
-                hostStart -= start;
-                portStart -= start;
-                pathStart -= 3 + start;
-                queryStart -= 3 + start;
-                fragmentStart -= 3 + start;
-                start = 0;
-                end = uri.length;
-              }
-            }
-          }
-        } else if (schemeEnd == start + 5 && uri.startsWith("https", start)) {
-          scheme = "https";
-          // HTTPS URIs should not have an explicit port of 443.
-          if (portStart > start && portStart + 4 == pathStart &&
-              uri.startsWith("443", portStart + 1)) {
-            if (start == 0 && end == uri.length) {
-              uri = uri.replaceRange(portStart, pathStart, "");
-              pathStart -= 4;
-              queryStart -= 4;
-              fragmentStart -= 4;
-              end -= 3;
-            } else {
-              uri = uri.substring(start, portStart) +
-                    uri.substring(pathStart, end);
-              schemeEnd -= start;
-              hostStart -= start;
-              portStart -= start;
-              pathStart -= 4 + start;
-              queryStart -= 4 + start;
-              fragmentStart -= 4 + start;
-              start = 0;
-              end = uri.length;
-            }
-          }
-        }
-      }
-    }
-
-    if (isSimple) {
-      if (start > 0 || end < uri.length) {
-        uri = uri.substring(start, end);
-        schemeEnd -= start;
-        hostStart -= start;
-        portStart -= start;
-        pathStart -= start;
-        queryStart -= start;
-        fragmentStart -= start;
-      }
-      return new _SimpleUri(uri, schemeEnd, hostStart, portStart, pathStart,
-                            queryStart, fragmentStart, scheme);
-
-    }
-
-    return new _Uri.notSimple(uri, start, end, schemeEnd, hostStart, portStart,
-                              pathStart, queryStart, fragmentStart, scheme);
-  }
-
-  /**
-   * Encode the string [component] using percent-encoding to make it
-   * safe for literal use as a URI component.
-   *
-   * All characters except uppercase and lowercase letters, digits and
-   * the characters `-_.!~*'()` are percent-encoded. This is the
-   * set of characters specified in RFC 2396 and the which is
-   * specified for the encodeUriComponent in ECMA-262 version 5.1.
-   *
-   * When manually encoding path segments or query components remember
-   * to encode each part separately before building the path or query
-   * string.
-   *
-   * For encoding the query part consider using
-   * [encodeQueryComponent].
-   *
-   * To avoid the need for explicitly encoding use the [pathSegments]
-   * and [queryParameters] optional named arguments when constructing
-   * a [Uri].
-   */
-  static String encodeComponent(String component) {
-    return _Uri._uriEncode(_Uri._unreserved2396Table, component, UTF8, false);
-  }
-
-  /**
-   * Encode the string [component] according to the HTML 4.01 rules
-   * for encoding the posting of a HTML form as a query string
-   * component.
-   *
-   * Encode the string [component] according to the HTML 4.01 rules
-   * for encoding the posting of a HTML form as a query string
-   * component.
-
-   * The component is first encoded to bytes using [encoding].
-   * The default is to use [UTF8] encoding, which preserves all
-   * the characters that don't need encoding.
-
-   * Then the resulting bytes are "percent-encoded". This transforms
-   * spaces (U+0020) to a plus sign ('+') and all bytes that are not
-   * the ASCII decimal digits, letters or one of '-._~' are written as
-   * a percent sign '%' followed by the two-digit hexadecimal
-   * representation of the byte.
-
-   * Note that the set of characters which are percent-encoded is a
-   * superset of what HTML 4.01 requires, since it refers to RFC 1738
-   * for reserved characters.
-   *
-   * When manually encoding query components remember to encode each
-   * part separately before building the query string.
-   *
-   * To avoid the need for explicitly encoding the query use the
-   * [queryParameters] optional named arguments when constructing a
-   * [Uri].
-   *
-   * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more
-   * details.
-   */
-  static String encodeQueryComponent(String component,
-                                     {Encoding encoding: UTF8}) {
-    return _Uri._uriEncode(_Uri._unreservedTable, component, encoding, true);
-  }
-
-  /**
-   * Decodes the percent-encoding in [encodedComponent].
-   *
-   * Note that decoding a URI component might change its meaning as
-   * some of the decoded characters could be characters with are
-   * delimiters for a given URI componene type. Always split a URI
-   * component using the delimiters for the component before decoding
-   * the individual parts.
-   *
-   * For handling the [path] and [query] components consider using
-   * [pathSegments] and [queryParameters] to get the separated and
-   * decoded component.
-   */
-  static String decodeComponent(String encodedComponent) {
-    return _Uri._uriDecode(encodedComponent, 0, encodedComponent.length,
-                           UTF8, false);
-  }
-
-  /**
-   * Decodes the percent-encoding in [encodedComponent], converting
-   * pluses to spaces.
-   *
-   * It will create a byte-list of the decoded characters, and then use
-   * [encoding] to decode the byte-list to a String. The default encoding is
-   * UTF-8.
-   */
-  static String decodeQueryComponent(
-      String encodedComponent,
-      {Encoding encoding: UTF8}) {
-    return _Uri._uriDecode(encodedComponent, 0, encodedComponent.length,
-                           encoding, true);
-  }
-
-  /**
-   * Encode the string [uri] using percent-encoding to make it
-   * safe for literal use as a full URI.
-   *
-   * All characters except uppercase and lowercase letters, digits and
-   * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This
-   * is the set of characters specified in in ECMA-262 version 5.1 for
-   * the encodeURI function .
-   */
-  static String encodeFull(String uri) {
-    return _Uri._uriEncode(_Uri._encodeFullTable, uri, UTF8, false);
-  }
-
-  /**
-   * Decodes the percent-encoding in [uri].
-   *
-   * Note that decoding a full URI might change its meaning as some of
-   * the decoded characters could be reserved characters. In most
-   * cases an encoded URI should be parsed into components using
-   * [Uri.parse] before decoding the separate components.
-   */
-  static String decodeFull(String uri) {
-    return _Uri._uriDecode(uri, 0, uri.length, UTF8, false);
-  }
-
-  /**
-   * Returns the [query] split into a map according to the rules
-   * specified for FORM post in the [HTML 4.01 specification section
-   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
-   * Each key and value in the returned map has been decoded. If the [query]
-   * is the empty string an empty map is returned.
-   *
-   * Keys in the query string that have no value are mapped to the
-   * empty string.
-   *
-   * Each query component will be decoded using [encoding]. The default encoding
-   * is UTF-8.
-   */
-  static Map<String, String> splitQueryString(String query,
-                                              {Encoding encoding: UTF8}) {
-    return query.split("&").fold({}, (map, element) {
-      int index = element.indexOf("=");
-      if (index == -1) {
-        if (element != "") {
-          map[decodeQueryComponent(element, encoding: encoding)] = "";
-        }
-      } else if (index != 0) {
-        var key = element.substring(0, index);
-        var value = element.substring(index + 1);
-        map[decodeQueryComponent(key, encoding: encoding)] =
-            decodeQueryComponent(value, encoding: encoding);
-      }
-      return map;
-    });
-  }
-
-
-  /**
-   * Parse the [host] as an IP version 4 (IPv4) address, returning the address
-   * as a list of 4 bytes in network byte order (big endian).
-   *
-   * Throws a [FormatException] if [host] is not a valid IPv4 address
-   * representation.
-   */
-  static List<int> parseIPv4Address(String host) =>
-       _parseIPv4Address(host, 0, host.length);
-
-  /// Implementation of [parseIPv4Address] that can work on a substring.
-  static List<int> _parseIPv4Address(String host, int start, int end) {
-    void error(String msg, int position) {
-      throw new FormatException('Illegal IPv4 address, $msg', host, position);
-    }
-
-    var result = new Uint8List(4);
-    int partIndex = 0;
-    int partStart = start;
-    for (int i = start; i < end; i++) {
-      int char = host.codeUnitAt(i);
-      if (char != _DOT) {
-        if (char ^ 0x30 > 9) {
-          // Fail on a non-digit character.
-          error("invalid character", i);
-        }
-      } else {
-        if (partIndex == 3) {
-          error('IPv4 address should contain exactly 4 parts', i);
-        }
-        int part = int.parse(host.substring(partStart, i));
-        if (part > 255) {
-          error("each part must be in the range 0..255", partStart);
-        }
-        result[partIndex++] = part;
-        partStart = i + 1;
-      }
-    }
-
-    if (partIndex != 3) {
-      error('IPv4 address should contain exactly 4 parts', end);
-    }
-
-    int part = int.parse(host.substring(partStart, end));
-    if (part > 255) {
-      error("each part must be in the range 0..255", partStart);
-    }
-    result[partIndex] = part;
-
-    return result;
-  }
-
-  /**
-   * Parse the [host] as an IP version 6 (IPv6) address, returning the address
-   * as a list of 16 bytes in network byte order (big endian).
-   *
-   * Throws a [FormatException] if [host] is not a valid IPv6 address
-   * representation.
-   *
-   * Acts on the substring from [start] to [end]. If [end] is omitted, it
-   * defaults ot the end of the string.
-   *
-   * Some examples of IPv6 addresses:
-   *  * ::1
-   *  * FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
-   *  * 3ffe:2a00:100:7031::1
-   *  * ::FFFF:129.144.52.38
-   *  * 2010:836B:4179::836B:4179
-   */
-  static List<int> parseIPv6Address(String host, [int start = 0, int end]) {
-    if (end == null) end = host.length;
-    // An IPv6 address consists of exactly 8 parts of 1-4 hex digits, separated
-    // by `:`'s, with the following exceptions:
-    //
-    //  - One (and only one) wildcard (`::`) may be present, representing a fill
-    //    of 0's. The IPv6 `::` is thus 16 bytes of `0`.
-    //  - The last two parts may be replaced by an IPv4 "dotted-quad" address.
-
-    // Helper function for reporting a badly formatted IPv6 address.
-    void error(String msg, [position]) {
-      throw new FormatException('Illegal IPv6 address, $msg', host, position);
-    }
-
-    // Parse a hex block.
-    int parseHex(int start, int end) {
-      if (end - start > 4) {
-        error('an IPv6 part can only contain a maximum of 4 hex digits', start);
-      }
-      int value = int.parse(host.substring(start, end), radix: 16);
-      if (value < 0 || value > 0xFFFF) {
-        error('each part must be in the range of `0x0..0xFFFF`', start);
-      }
-      return value;
-    }
-
-    if (host.length < 2) error('address is too short');
-    List<int> parts = [];
-    bool wildcardSeen = false;
-    // Set if seeing a ".", suggesting that there is an IPv4 address.
-    bool seenDot = false;
-    int partStart = start;
-    // Parse all parts, except a potential last one.
-    for (int i = start; i < end; i++) {
-      int char = host.codeUnitAt(i);
-      if (char == _COLON) {
-        if (i == start) {
-          // If we see a `:` in the beginning, expect wildcard.
-          i++;
-          if (host.codeUnitAt(i) != _COLON) {
-            error('invalid start colon.', i);
-          }
-          partStart = i;
-        }
-        if (i == partStart) {
-          // Wildcard. We only allow one.
-          if (wildcardSeen) {
-            error('only one wildcard `::` is allowed', i);
-          }
-          wildcardSeen = true;
-          parts.add(-1);
-        } else {
-          // Found a single colon. Parse [partStart..i] as a hex entry.
-          parts.add(parseHex(partStart, i));
-        }
-        partStart = i + 1;
-      } else if (char == _DOT) {
-        seenDot = true;
-      }
-    }
-    if (parts.length == 0) error('too few parts');
-    bool atEnd = (partStart == end);
-    bool isLastWildcard = (parts.last == -1);
-    if (atEnd && !isLastWildcard) {
-      error('expected a part after last `:`', end);
-    }
-    if (!atEnd) {
-      if (!seenDot) {
-        parts.add(parseHex(partStart, end));
-      } else {
-        List<int> last = _parseIPv4Address(host, partStart, end);
-        parts.add(last[0] << 8 | last[1]);
-        parts.add(last[2] << 8 | last[3]);
-      }
-    }
-    if (wildcardSeen) {
-      if (parts.length > 7) {
-        error('an address with a wildcard must have less than 7 parts');
-      }
-    } else if (parts.length != 8) {
-      error('an address without a wildcard must contain exactly 8 parts');
-    }
-    List<int> bytes = new Uint8List(16);
-    for (int i = 0, index = 0; i < parts.length; i++) {
-      int value = parts[i];
-      if (value == -1) {
-        int wildCardLength = 9 - parts.length;
-        for (int j = 0; j < wildCardLength; j++) {
-          bytes[index] = 0;
-          bytes[index + 1] = 0;
-          index += 2;
-        }
-      } else {
-        bytes[index] = value >> 8;
-        bytes[index + 1] = value & 0xff;
-        index += 2;
-      }
-    }
-    return bytes;
-  }
-}
-
-class _Uri implements Uri {
-  // We represent the missing scheme as an empty string.
-  // A valid scheme cannot be empty.
-  final String scheme;
-
-  /**
-   * The user-info part of the authority.
-   *
-   * Does not distinguish between an empty user-info and an absent one.
-   * The value is always non-null.
-   * Is considered absent if [_host] is `null`.
-   */
-  final String _userInfo;
-
-  /**
-   * The host name of the URI.
-   *
-   * Set to `null` if there is no authority in the URI.
-   * The host name is the only mandatory part of an authority, so we use
-   * it to mark whether an authority part was present or not.
-   */
-  final String _host;
-
-  /**
-   * The port number part of the authority.
-   *
-   * The port. Set to null if there is no port. Normalized to null if
-   * the port is the default port for the scheme.
-   */
-  int _port;
-
-  /**
-   * The path of the URI.
-   *
-   * Always non-null.
-   */
-  String _path;
-
-  // The query content, or null if there is no query.
-  final String _query;
-
-  // The fragment content, or null if there is no fragment.
-  final String _fragment;
-
-  /**
-   * Cache the computed return value of [pathSegements].
-   */
-  List<String> _pathSegments;
-
-  /**
-   * Cache of the full normalized text representation of the URI.
-   */
-  String _text;
-
-  /**
-   * Cache of the hashCode of [_text].
-   *
-   * Is null until computed.
-   */
-  int _hashCodeCache;
-
-  /**
-   * Cache the computed return value of [queryParameters].
-   */
-  Map<String, String> _queryParameters;
-  Map<String, List<String>> _queryParameterLists;
-
-  /// Internal non-verifying constructor. Only call with validated arguments.
-  _Uri._internal(this.scheme,
-                 this._userInfo,
-                 this._host,
-                 this._port,
-                 this._path,
-                 this._query,
-                 this._fragment);
-
-  /// Create a [_Uri] from parts of [uri].
-  ///
-  /// The parameters specify the start/end of particular components of the URI.
-  /// The [scheme] may contain a string representing a normalized scheme
-  /// component if one has already been discovered.
-  factory _Uri.notSimple(String uri, int start, int end, int schemeEnd,
-                        int hostStart, int portStart, int pathStart,
-                        int queryStart, int fragmentStart, String scheme) {
-    if (scheme == null) {
-      scheme = "";
-      if (schemeEnd > start) {
-        scheme = _makeScheme(uri, start, schemeEnd);
-      } else if (schemeEnd == start) {
-        _fail(uri, start, "Invalid empty scheme");
-      }
-    }
-    String userInfo = "";
-    String host;
-    int port;
-    if (hostStart > start) {
-      int userInfoStart = schemeEnd + 3;
-      if (userInfoStart < hostStart) {
-        userInfo = _makeUserInfo(uri, userInfoStart, hostStart - 1);
-      }
-      host = _makeHost(uri, hostStart, portStart, false);
-      if (portStart + 1 < pathStart) {
-        // Should throw because invalid.
-        port = int.parse(uri.substring(portStart + 1, pathStart), onError: (_) {
-          throw new FormatException("Invalid port", uri, portStart + 1);
-        });
-        port = _makePort(port, scheme);
-      }
-    }
-    String path = _makePath(uri, pathStart, queryStart, null,
-                            scheme, host != null);
-    String query;
-    if (queryStart < fragmentStart) {
-      query = _makeQuery(uri, queryStart + 1, fragmentStart, null);
-    }
-    String fragment;
-    if (fragmentStart < end) {
-      fragment = _makeFragment(uri, fragmentStart + 1, end);
-    }
-    return new _Uri._internal(scheme,
-                              userInfo,
-                              host,
-                              port,
-                              path,
-                              query,
-                              fragment);
-  }
-
-  /// Implementation of [Uri.Uri].
-  factory _Uri({String scheme,
-                String userInfo,
-                String host,
-                int port,
-                String path,
-                Iterable<String> pathSegments,
-                String query,
-                Map<String, dynamic/*String|Iterable<String>*/> queryParameters,
-                String fragment}) {
-    scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme));
-    userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo));
-    host = _makeHost(host, 0, _stringOrNullLength(host), false);
-    // Special case this constructor for backwards compatibility.
-    if (query == "") query = null;
-    query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
-    fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment));
-    port = _makePort(port, scheme);
-    bool isFile = (scheme == "file");
-    if (host == null &&
-        (userInfo.isNotEmpty || port != null || isFile)) {
-      host = "";
-    }
-    bool hasAuthority = (host != null);
-    path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
-                     scheme, hasAuthority);
-    if (scheme.isEmpty && host == null && !path.startsWith('/')) {
-      path = _normalizeRelativePath(path);
-    } else {
-      path = _removeDotSegments(path);
-    }
-    return new _Uri._internal(scheme, userInfo, host, port,
-                              path, query, fragment);
-  }
-
-  /// Implementation of [Uri.http].
-  factory _Uri.http(String authority,
-                    String unencodedPath,
-                    [Map<String, String> queryParameters]) {
-    return _makeHttpUri("http", authority, unencodedPath, queryParameters);
-  }
-
-  /// Implementation of [Uri.https].
-  factory _Uri.https(String authority,
-                     String unencodedPath,
-                     [Map<String, String> queryParameters]) {
-    return _makeHttpUri("https", authority, unencodedPath, queryParameters);
-  }
-
-  String get authority {
-    if (!hasAuthority) return "";
-    var sb = new StringBuffer();
-    _writeAuthority(sb);
-    return sb.toString();
-  }
-
-  String get userInfo => _userInfo;
-
-  String get host {
-    if (_host == null) return "";
-    if (_host.startsWith('[')) {
-      return _host.substring(1, _host.length - 1);
-    }
-    return _host;
-  }
-
-  int get port {
-    if (_port == null) return _defaultPort(scheme);
-    return _port;
-  }
-
-  // The default port for the scheme of this Uri.
-  static int _defaultPort(String scheme) {
-    if (scheme == "http") return 80;
-    if (scheme == "https") return 443;
-    return 0;
-  }
-
-  String get path => _path;
-
-  String get query => _query ?? "";
-
-  String get fragment => _fragment ?? "";
-
-  // Report a parse failure.
-  static void _fail(String uri, int index, String message) {
-    throw new FormatException(message, uri, index);
-  }
-
-  static Uri _makeHttpUri(String scheme,
-                          String authority,
-                          String unencodedPath,
-                          Map<String, String> queryParameters) {
-    var userInfo = "";
-    var host = null;
-    var port = null;
-
-    if (authority != null && authority.isNotEmpty) {
-      var hostStart = 0;
-      // Split off the user info.
-      bool hasUserInfo = false;
-      for (int i = 0; i < authority.length; i++) {
-        const int atSign = 0x40;
-        if (authority.codeUnitAt(i) == atSign) {
-          hasUserInfo = true;
-          userInfo = authority.substring(0, i);
-          hostStart = i + 1;
-          break;
-        }
-      }
-      var hostEnd = hostStart;
-      if (hostStart < authority.length &&
-          authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
-        // IPv6 host.
-        for (; hostEnd < authority.length; hostEnd++) {
-          if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
-        }
-        if (hostEnd == authority.length) {
-          throw new FormatException("Invalid IPv6 host entry.",
-                                    authority, hostStart);
-        }
-        Uri.parseIPv6Address(authority, hostStart + 1, hostEnd);
-        hostEnd++;  // Skip the closing bracket.
-        if (hostEnd != authority.length &&
-            authority.codeUnitAt(hostEnd) != _COLON) {
-          throw new FormatException("Invalid end of authority",
-                                    authority, hostEnd);
-        }
-      }
-      // Split host and port.
-      bool hasPort = false;
-      for (; hostEnd < authority.length; hostEnd++) {
-        if (authority.codeUnitAt(hostEnd) == _COLON) {
-          var portString = authority.substring(hostEnd + 1);
-          // We allow the empty port - falling back to initial value.
-          if (portString.isNotEmpty) port = int.parse(portString);
-          break;
-        }
-      }
-      host = authority.substring(hostStart, hostEnd);
-    }
-    return new Uri(scheme: scheme,
-                   userInfo: userInfo,
-                   host: host,
-                   port: port,
-                   pathSegments: unencodedPath.split("/"),
-                   queryParameters: queryParameters);
-  }
-
-  /// Implementation of [Uri.file].
-  factory _Uri.file(String path, {bool windows}) {
-    windows = (windows == null) ? _Uri._isWindows : windows;
-    return windows ? _makeWindowsFileUrl(path, false)
-                   : _makeFileUri(path, false);
-  }
-
-  /// Implementation of [Uri.directory].
-  factory _Uri.directory(String path, {bool windows}) {
-    windows = (windows == null) ? _Uri._isWindows : windows;
-    return windows ? _makeWindowsFileUrl(path, true)
-                   : _makeFileUri(path, true);
-  }
-
+  external static Uri get base;
 
-  /// Used internally in path-related constructors.
   external static bool get _isWindows;
 
   static _checkNonWindowsPathReservedCharacters(List<String> segments,
@@ -1740,6 +969,46 @@
     }
   }
 
+  /**
+   * Returns a new `Uri` based on this one, but with some parts replaced.
+   *
+   * This method takes the same parameters as the [new Uri] constructor,
+   * and they have the same meaning.
+   *
+   * At most one of [path] and [pathSegments] must be provided.
+   * Likewise, at most one of [query] and [queryParameters] must be provided.
+   *
+   * Each part that is not provided will default to the corresponding
+   * value from this `Uri` instead.
+   *
+   * This method is different from [Uri.resolve] which overrides in a
+   * hierarchial manner,
+   * and can instead replace each part of a `Uri` individually.
+   *
+   * Example:
+   *
+   *     Uri uri1 = Uri.parse("a://b@c:4/d/e?f#g");
+   *     Uri uri2 = uri1.replace(scheme: "A", path: "D/E/E", fragment: "G");
+   *     print(uri2);  // prints "A://b@c:4/D/E/E/?f#G"
+   *
+   * This method acts similarly to using the `new Uri` constructor with
+   * some of the arguments taken from this `Uri` . Example:
+   *
+   *     Uri uri3 = new Uri(
+   *         scheme: "A",
+   *         userInfo: uri1.userInfo,
+   *         host: uri1.host,
+   *         port: uri1.port,
+   *         path: "D/E/E",
+   *         query: uri1.query,
+   *         fragment: "G");
+   *     print(uri3);  // prints "A://b@c:4/D/E/E/?f#G"
+   *     print(uri2 == uri3);  // prints true.
+   *
+   * Using this method can be seen as a shorthand for the `Uri` constructor
+   * call above, but may also be slightly faster because the parts taken
+   * from this `Uri` need not be checked for validity again.
+   */
   Uri replace({String scheme,
                String userInfo,
                String host,
@@ -1806,16 +1075,29 @@
       fragment = this._fragment;
     }
 
-    return new _Uri._internal(
+    return new Uri._internal(
         scheme, userInfo, host, port, path, query, fragment);
   }
 
+  /**
+   * Returns a `Uri` that differs from this only in not having a fragment.
+   *
+   * If this `Uri` does not have a fragment, it is itself returned.
+   */
   Uri removeFragment() {
     if (!this.hasFragment) return this;
-    return new _Uri._internal(scheme, _userInfo, _host, _port,
+    return new Uri._internal(scheme, _userInfo, _host, _port,
                              _path, _query, null);
   }
 
+  /**
+   * Returns the URI path split into its segments. Each of the segments in the
+   * returned list have been decoded. If the path is empty the empty list will
+   * be returned. A leading slash `/` does not affect the segments returned.
+   *
+   * The returned list is unmodifiable and will throw [UnsupportedError] on any
+   * calls that would mutate it.
+   */
   List<String> get pathSegments {
     var result = _pathSegments;
     if (result != null) return result;
@@ -1832,14 +1114,43 @@
     return result;
   }
 
+  /**
+   * Returns the URI query split into a map according to the rules
+   * specified for FORM post in the [HTML 4.01 specification section
+   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
+   * Each key and value in the returned map has been decoded.
+   * If there is no query the empty map is returned.
+   *
+   * Keys in the query string that have no value are mapped to the
+   * empty string.
+   * If a key occurs more than once in the query string, it is mapped to
+   * an arbitrary choice of possible value.
+   * The [queryParametersAll] getter can provide a map
+   * that maps keys to all of their values.
+   *
+   * The returned map is unmodifiable.
+   */
   Map<String, String> get queryParameters {
     if (_queryParameters == null) {
       _queryParameters =
-          new UnmodifiableMapView<String, String>(Uri.splitQueryString(query));
+          new UnmodifiableMapView<String, String>(splitQueryString(query));
     }
     return _queryParameters;
   }
 
+  /**
+   * Returns the URI query split into a map according to the rules
+   * specified for FORM post in the [HTML 4.01 specification section
+   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
+   * Each key and value in the returned map has been decoded. If there is no
+   * query the empty map is returned.
+   *
+   * Keys are mapped to lists of their values. If a key occurs only once,
+   * its value is a singleton list. If a key occurs with no value, the
+   * empty string is used as the value for that occurrence.
+   *
+   * The returned map and the lists it contains are unmodifiable.
+   */
   Map<String, List<String>> get queryParametersAll {
     if (_queryParameterLists == null) {
       Map queryParameterLists = _splitQueryStringAll(query);
@@ -1853,6 +1164,22 @@
     return _queryParameterLists;
   }
 
+  /**
+   * Returns a URI where the path has been normalized.
+   *
+   * A normalized path does not contain `.` segments or non-leading `..`
+   * segments.
+   * Only a relative path with no scheme or authority may contain
+   * leading `..` segments,
+   * a path that starts with `/` will also drop any leading `..` segments.
+   *
+   * This uses the same normalization strategy as `new Uri().resolve(this)`.
+   *
+   * Does not change any part of the URI except the path.
+   *
+   * The default implementation of `Uri` always normalizes paths, so calling
+   * this function has no effect.
+   */
   Uri normalizePath() {
     String path = _normalizePath(_path, scheme, hasAuthority);
     if (identical(path, _path)) return this;
@@ -1885,7 +1212,7 @@
       if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) {
         _fail(host, start, 'Missing end `]` to match `[` in host');
       }
-      Uri.parseIPv6Address(host, start + 1, end - 1);
+      parseIPv6Address(host, start + 1, end - 1);
       // RFC 5952 requires hex digits to be lower case.
       return host.substring(start, end).toLowerCase();
     }
@@ -1893,7 +1220,7 @@
       // TODO(lrn): skip if too short to be a valid IPv6 address?
       for (int i = start; i < end; i++) {
         if (host.codeUnitAt(i) == _COLON) {
-          Uri.parseIPv6Address(host, start, end);
+          parseIPv6Address(host, start, end);
           return '[$host]';
         }
       }
@@ -2006,17 +1333,6 @@
     }
     scheme = scheme.substring(start, end);
     if (containsUpperCase) scheme = scheme.toLowerCase();
-    return _canonicalizeScheme(scheme);
-  }
-
-  // Canonicalize a few often-used scheme strings.
-  //
-  // This improves memory usage and makes comparison faster.
-  static String _canonicalizeScheme(String scheme) {
-    if (scheme == "http") return "http";
-    if (scheme == "file") return "file";
-    if (scheme == "https") return "https";
-    if (scheme == "package") return "package";
     return scheme;
   }
 
@@ -2103,6 +1419,8 @@
     return _normalize(fragment, start, end, _queryCharTable);
   }
 
+  static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length;
+
   /**
    * Performs RFC 3986 Percent-Encoding Normalization.
    *
@@ -2147,11 +1465,10 @@
   // Converts a UTF-16 code-unit to its value as a hex digit.
   // Returns -1 for non-hex digits.
   static int _parseHexDigit(int char) {
-    const int zeroDigit = 0x30;
-    int digit = char ^ zeroDigit;
+    int digit = char ^ Uri._ZERO;
     if (digit <= 9) return digit;
     int lowerCase = char | 0x20;
-    if (_LOWER_CASE_A <= lowerCase && lowerCase <= _LOWER_CASE_F) {
+    if (Uri._LOWER_CASE_A <= lowerCase && lowerCase <= _LOWER_CASE_F) {
       return lowerCase - (_LOWER_CASE_A - 10);
     }
     return -1;
@@ -2237,7 +1554,7 @@
             if (index + 1 < end) {
               int tail = component.codeUnitAt(index + 1);
               if ((tail & 0xFC00) == 0xDC00) {
-                // Tail surrogate.
+                // Tail surrogat.
                 sourceLength = 2;
                 char = 0x10000 | ((char & 0x3ff) << 10) | (tail & 0x3ff);
               }
@@ -2386,10 +1703,34 @@
     return output.join("/");
   }
 
+  /**
+   * Resolve [reference] as an URI relative to `this`.
+   *
+   * First turn [reference] into a URI using [Uri.parse]. Then resolve the
+   * resulting URI relative to `this`.
+   *
+   * Returns the resolved URI.
+   *
+   * See [resolveUri] for details.
+   */
   Uri resolve(String reference) {
     return resolveUri(Uri.parse(reference));
   }
 
+  /**
+   * Resolve [reference] as an URI relative to `this`.
+   *
+   * Returns the resolved URI.
+   *
+   * The algorithm "Transform Reference" for resolving a reference is described
+   * in [RFC-3986 Section 5](http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123").
+   *
+   * Updated to handle the case where the base URI is just a relative path -
+   * that is: when it has no scheme or authority and the path does not start
+   * with a slash.
+   * In that case, the paths are combined without removing leading "..", and
+   * an empty path is not converted to "/".
+   */
   Uri resolveUri(Uri reference) {
     // From RFC 3986.
     String targetScheme;
@@ -2435,17 +1776,11 @@
           } else {
             // This is the RFC 3986 behavior for merging.
             if (this.hasEmptyPath) {
-              if (!this.hasAuthority) {
-                if (!this.hasScheme) {
-                  // Keep the path relative if no scheme or authority.
-                  targetPath = reference.path;
-                } else {
-                  // Remove leading dot-segments if the path is put
-                  // beneath a scheme.
-                  targetPath = _removeDotSegments(reference.path);
-                }
+              if (!this.hasScheme && !this.hasAuthority) {
+                // Keep the path relative if no scheme or authority.
+                targetPath = reference.path;
               } else {
-                // RFC algorithm for base with authority and empty path.
+                // Add path normalization on top of RFC algorithm.
                 targetPath = _removeDotSegments("/" + reference.path);
               }
             } else {
@@ -2453,9 +1788,8 @@
               if (this.hasScheme || this.hasAuthority || this.hasAbsolutePath) {
                 targetPath = _removeDotSegments(mergedPath);
               } else {
-                // Non-RFC 3986 behavior.
-                // If both base and reference are relative paths,
-                // allow the merged path to start with "..".
+                // Non-RFC 3986 beavior. If both base and reference are relative
+                // path, allow the merged path to start with "..".
                 // The RFC only specifies the case where the base has a scheme.
                 targetPath = _normalizeRelativePath(mergedPath);
               }
@@ -2466,29 +1800,63 @@
       }
     }
     String fragment = reference.hasFragment ? reference.fragment : null;
-    return new _Uri._internal(targetScheme,
-                              targetUserInfo,
-                              targetHost,
-                              targetPort,
-                              targetPath,
-                              targetQuery,
-                              fragment);
+    return new Uri._internal(targetScheme,
+                             targetUserInfo,
+                             targetHost,
+                             targetPort,
+                             targetPath,
+                             targetQuery,
+                             fragment);
   }
 
+  /**
+   * Returns whether the URI has a [scheme] component.
+   */
   bool get hasScheme => scheme.isNotEmpty;
 
+  /**
+   * Returns whether the URI has an [authority] component.
+   */
   bool get hasAuthority => _host != null;
 
+  /**
+   * Returns whether the URI has an explicit port.
+   *
+   * If the port number is the default port number
+   * (zero for unrecognized schemes, with http (80) and https (443) being
+   * recognized),
+   * then the port is made implicit and omitted from the URI.
+   */
   bool get hasPort => _port != null;
 
+  /**
+   * Returns whether the URI has a query part.
+   */
   bool get hasQuery => _query != null;
 
+  /**
+   * Returns whether the URI has a fragment part.
+   */
   bool get hasFragment => _fragment != null;
 
+  /**
+   * Returns whether the URI has an empty path.
+   */
   bool get hasEmptyPath => _path.isEmpty;
 
+  /**
+   * Returns whether the URI has an absolute path (starting with '/').
+   */
   bool get hasAbsolutePath => _path.startsWith('/');
 
+  /**
+   * Returns the origin of the URI in the form scheme://host:port for the
+   * schemes http and https.
+   *
+   * It is an error if the scheme is not "http" or "https".
+   *
+   * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
+   */
   String get origin {
     if (scheme == "" || _host == null || _host == "") {
       throw new StateError("Cannot use origin without a scheme: $this");
@@ -2501,6 +1869,69 @@
     return "$scheme://$_host:$_port";
   }
 
+  /**
+   * Returns the file path from a file URI.
+   *
+   * The returned path has either Windows or non-Windows
+   * semantics.
+   *
+   * For non-Windows semantics the slash ("/") is used to separate
+   * path segments.
+   *
+   * For Windows semantics the backslash ("\") separator is used to
+   * separate path segments.
+   *
+   * If the URI is absolute the path starts with a path separator
+   * unless Windows semantics is used and the first path segment is a
+   * drive letter. When Windows semantics is used a host component in
+   * the uri in interpreted as a file server and a UNC path is
+   * returned.
+   *
+   * The default for whether to use Windows or non-Windows semantics
+   * determined from the platform Dart is running on. When running in
+   * the standalone VM this is detected by the VM based on the
+   * operating system. When running in a browser non-Windows semantics
+   * is always used.
+   *
+   * To override the automatic detection of which semantics to use pass
+   * a value for [windows]. Passing `true` will use Windows
+   * semantics and passing `false` will use non-Windows semantics.
+   *
+   * If the URI ends with a slash (i.e. the last path component is
+   * empty) the returned file path will also end with a slash.
+   *
+   * With Windows semantics URIs starting with a drive letter cannot
+   * be relative to the current drive on the designated drive. That is
+   * for the URI `file:///c:abc` calling `toFilePath` will throw as a
+   * path segment cannot contain colon on Windows.
+   *
+   * Examples using non-Windows semantics (resulting of calling
+   * toFilePath in comment):
+   *
+   *     Uri.parse("xxx/yyy");  // xxx/yyy
+   *     Uri.parse("xxx/yyy/");  // xxx/yyy/
+   *     Uri.parse("file:///xxx/yyy");  // /xxx/yyy
+   *     Uri.parse("file:///xxx/yyy/");  // /xxx/yyy/
+   *     Uri.parse("file:///C:");  // /C:
+   *     Uri.parse("file:///C:a");  // /C:a
+   *
+   * Examples using Windows semantics (resulting URI in comment):
+   *
+   *     Uri.parse("xxx/yyy");  // xxx\yyy
+   *     Uri.parse("xxx/yyy/");  // xxx\yyy\
+   *     Uri.parse("file:///xxx/yyy");  // \xxx\yyy
+   *     Uri.parse("file:///xxx/yyy/");  // \xxx\yyy/
+   *     Uri.parse("file:///C:/xxx/yyy");  // C:\xxx\yyy
+   *     Uri.parse("file:C:xxx/yyy");  // Throws as a path segment
+   *                                   // cannot contain colon on Windows.
+   *     Uri.parse("file://server/share/file");  // \\server\share\file
+   *
+   * If the URI is not a file URI calling this throws
+   * [UnsupportedError].
+   *
+   * If the URI cannot be converted to a file path calling this throws
+   * [UnsupportedError].
+   */
   String toFilePath({bool windows}) {
     if (scheme != "" && scheme != "file") {
       throw new UnsupportedError(
@@ -2515,27 +1946,25 @@
           "Cannot extract a file path from a URI with a fragment component");
     }
     if (windows == null) windows = _isWindows;
-    return windows ? _toWindowsFilePath(this) : _toFilePath();
+    return windows ? _toWindowsFilePath() : _toFilePath();
   }
 
   String _toFilePath() {
-    if (hasAuthority && host != "") {
+    if (host != "") {
       throw new UnsupportedError(
           "Cannot extract a non-Windows file path from a file URI "
           "with an authority");
     }
-    // Use path segments to have any escapes unescaped.
-    var pathSegments = this.pathSegments;
     _checkNonWindowsPathReservedCharacters(pathSegments, false);
     var result = new StringBuffer();
-    if (hasAbsolutePath) result.write("/");
+    if (_isPathAbsolute) result.write("/");
     result.writeAll(pathSegments, "/");
     return result.toString();
   }
 
-  static String _toWindowsFilePath(Uri uri) {
+  String _toWindowsFilePath() {
     bool hasDriveLetter = false;
-    var segments = uri.pathSegments;
+    var segments = pathSegments;
     if (segments.length > 0 &&
         segments[0].length == 2 &&
         segments[0].codeUnitAt(1) == _COLON) {
@@ -2543,25 +1972,23 @@
       _checkWindowsPathReservedCharacters(segments, false, 1);
       hasDriveLetter = true;
     } else {
-      _checkWindowsPathReservedCharacters(segments, false, 0);
+      _checkWindowsPathReservedCharacters(segments, false);
     }
     var result = new StringBuffer();
-    if (uri.hasAbsolutePath && !hasDriveLetter) result.write(r"\");
-    if (uri.hasAuthority) {
-      var host = uri.host;
-      if (host.isNotEmpty) {
-        result.write(r"\");
-        result.write(host);
-        result.write(r"\");
-      }
+    if (_isPathAbsolute && !hasDriveLetter) result.write("\\");
+    if (host != "") {
+      result.write("\\");
+      result.write(host);
+      result.write("\\");
     }
-    result.writeAll(segments, r"\");
-    if (hasDriveLetter && segments.length == 1) result.write(r"\");
+    result.writeAll(segments, "\\");
+    if (hasDriveLetter && segments.length == 1) result.write("\\");
     return result.toString();
   }
 
   bool get _isPathAbsolute {
-    return _path != null && _path.startsWith('/');
+    if (path == null || path.isEmpty) return false;
+    return path.startsWith('/');
   }
 
   void _writeAuthority(StringSink ss) {
@@ -2587,13 +2014,8 @@
   UriData get data => (scheme == "data") ? new UriData.fromUri(this) : null;
 
   String toString() {
-    return _text ??= _initializeText();
-  }
-
-  String _initializeText() {
-    assert(_text == null);
     StringBuffer sb = new StringBuffer();
-    if (scheme.isNotEmpty) sb..write(scheme)..write(":");
+    _addIfNonEmpty(sb, scheme, scheme, ':');
     if (hasAuthority || path.startsWith("//") || (scheme == "file")) {
       // File URIS always have the authority, even if it is empty.
       // The empty URI means "localhost".
@@ -2601,31 +2023,192 @@
       _writeAuthority(sb);
     }
     sb.write(path);
-    if (_query != null) sb..write("?")..write(_query);
-    if (_fragment != null) sb..write("#")..write(_fragment);
+    if (_query != null) { sb..write("?")..write(_query); }
+    if (_fragment != null) { sb..write("#")..write(_fragment); }
     return sb.toString();
   }
 
   bool operator==(other) {
-    if (identical(this, other)) return true;
-    if (other is Uri) {
-      Uri uri = other;
-      return scheme       == uri.scheme       &&
-             hasAuthority == uri.hasAuthority &&
-             userInfo     == uri.userInfo     &&
-             host         == uri.host         &&
-             port         == uri.port         &&
-             path         == uri.path         &&
-             hasQuery     == uri.hasQuery     &&
-             query        == uri.query        &&
-             hasFragment  == uri.hasFragment  &&
-             fragment     == uri.fragment;
-    }
-    return false;
+    if (other is! Uri) return false;
+    Uri uri = other;
+    return scheme       == uri.scheme       &&
+           hasAuthority == uri.hasAuthority &&
+           userInfo     == uri.userInfo     &&
+           host         == uri.host         &&
+           port         == uri.port         &&
+           path         == uri.path         &&
+           hasQuery     == uri.hasQuery     &&
+           query        == uri.query        &&
+           hasFragment  == uri.hasFragment  &&
+           fragment     == uri.fragment;
   }
 
   int get hashCode {
-    return _hashCodeCache ??= toString().hashCode;
+    int combine(part, current) {
+      // The sum is truncated to 30 bits to make sure it fits into a Smi.
+      return (current * 31 + part.hashCode) & 0x3FFFFFFF;
+    }
+    return combine(scheme, combine(userInfo, combine(host, combine(port,
+        combine(path, combine(query, combine(fragment, 1)))))));
+  }
+
+  static void _addIfNonEmpty(StringBuffer sb, String test,
+                             String first, String second) {
+    if ("" != test) {
+      sb.write(first);
+      sb.write(second);
+    }
+  }
+
+  /**
+   * Encode the string [component] using percent-encoding to make it
+   * safe for literal use as a URI component.
+   *
+   * All characters except uppercase and lowercase letters, digits and
+   * the characters `-_.!~*'()` are percent-encoded. This is the
+   * set of characters specified in RFC 2396 and the which is
+   * specified for the encodeUriComponent in ECMA-262 version 5.1.
+   *
+   * When manually encoding path segments or query components remember
+   * to encode each part separately before building the path or query
+   * string.
+   *
+   * For encoding the query part consider using
+   * [encodeQueryComponent].
+   *
+   * To avoid the need for explicitly encoding use the [pathSegments]
+   * and [queryParameters] optional named arguments when constructing
+   * a [Uri].
+   */
+  static String encodeComponent(String component) {
+    return _uriEncode(_unreserved2396Table, component, UTF8, false);
+  }
+
+  /**
+   * Encode the string [component] according to the HTML 4.01 rules
+   * for encoding the posting of a HTML form as a query string
+   * component.
+   *
+   * Encode the string [component] according to the HTML 4.01 rules
+   * for encoding the posting of a HTML form as a query string
+   * component.
+
+   * The component is first encoded to bytes using [encoding].
+   * The default is to use [UTF8] encoding, which preserves all
+   * the characters that don't need encoding.
+
+   * Then the resulting bytes are "percent-encoded". This transforms
+   * spaces (U+0020) to a plus sign ('+') and all bytes that are not
+   * the ASCII decimal digits, letters or one of '-._~' are written as
+   * a percent sign '%' followed by the two-digit hexadecimal
+   * representation of the byte.
+
+   * Note that the set of characters which are percent-encoded is a
+   * superset of what HTML 4.01 requires, since it refers to RFC 1738
+   * for reserved characters.
+   *
+   * When manually encoding query components remember to encode each
+   * part separately before building the query string.
+   *
+   * To avoid the need for explicitly encoding the query use the
+   * [queryParameters] optional named arguments when constructing a
+   * [Uri].
+   *
+   * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more
+   * details.
+   */
+  static String encodeQueryComponent(String component,
+                                     {Encoding encoding: UTF8}) {
+    return _uriEncode(_unreservedTable, component, encoding, true);
+  }
+
+  /**
+   * Decodes the percent-encoding in [encodedComponent].
+   *
+   * Note that decoding a URI component might change its meaning as
+   * some of the decoded characters could be characters with are
+   * delimiters for a given URI componene type. Always split a URI
+   * component using the delimiters for the component before decoding
+   * the individual parts.
+   *
+   * For handling the [path] and [query] components consider using
+   * [pathSegments] and [queryParameters] to get the separated and
+   * decoded component.
+   */
+  static String decodeComponent(String encodedComponent) {
+    return _uriDecode(encodedComponent, 0, encodedComponent.length,
+                      UTF8, false);
+  }
+
+  /**
+   * Decodes the percent-encoding in [encodedComponent], converting
+   * pluses to spaces.
+   *
+   * It will create a byte-list of the decoded characters, and then use
+   * [encoding] to decode the byte-list to a String. The default encoding is
+   * UTF-8.
+   */
+  static String decodeQueryComponent(
+      String encodedComponent,
+      {Encoding encoding: UTF8}) {
+    return _uriDecode(encodedComponent, 0, encodedComponent.length,
+                      encoding, true);
+  }
+
+  /**
+   * Encode the string [uri] using percent-encoding to make it
+   * safe for literal use as a full URI.
+   *
+   * All characters except uppercase and lowercase letters, digits and
+   * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This
+   * is the set of characters specified in in ECMA-262 version 5.1 for
+   * the encodeURI function .
+   */
+  static String encodeFull(String uri) {
+    return _uriEncode(_encodeFullTable, uri, UTF8, false);
+  }
+
+  /**
+   * Decodes the percent-encoding in [uri].
+   *
+   * Note that decoding a full URI might change its meaning as some of
+   * the decoded characters could be reserved characters. In most
+   * cases an encoded URI should be parsed into components using
+   * [Uri.parse] before decoding the separate components.
+   */
+  static String decodeFull(String uri) {
+    return _uriDecode(uri, 0, uri.length, UTF8, false);
+  }
+
+  /**
+   * Returns the [query] split into a map according to the rules
+   * specified for FORM post in the [HTML 4.01 specification section
+   * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4").
+   * Each key and value in the returned map has been decoded. If the [query]
+   * is the empty string an empty map is returned.
+   *
+   * Keys in the query string that have no value are mapped to the
+   * empty string.
+   *
+   * Each query component will be decoded using [encoding]. The default encoding
+   * is UTF-8.
+   */
+  static Map<String, String> splitQueryString(String query,
+                                              {Encoding encoding: UTF8}) {
+    return query.split("&").fold({}, (map, element) {
+      int index = element.indexOf("=");
+      if (index == -1) {
+        if (element != "") {
+          map[decodeQueryComponent(element, encoding: encoding)] = "";
+        }
+      } else if (index != 0) {
+        var key = element.substring(0, index);
+        var value = element.substring(index + 1);
+        map[Uri.decodeQueryComponent(key, encoding: encoding)] =
+            decodeQueryComponent(value, encoding: encoding);
+      }
+      return map;
+    });
   }
 
   static List _createList() => [];
@@ -2668,6 +2251,175 @@
     return result;
   }
 
+  /**
+   * Parse the [host] as an IP version 4 (IPv4) address, returning the address
+   * as a list of 4 bytes in network byte order (big endian).
+   *
+   * Throws a [FormatException] if [host] is not a valid IPv4 address
+   * representation.
+   */
+  static List<int> parseIPv4Address(String host) {
+    void error(String msg) {
+      throw new FormatException('Illegal IPv4 address, $msg');
+    }
+    var bytes = host.split('.');
+    if (bytes.length != 4) {
+      error('IPv4 address should contain exactly 4 parts');
+    }
+    // TODO(ajohnsen): Consider using Uint8List.
+    return bytes
+        .map((byteString) {
+          int byte = int.parse(byteString);
+          if (byte < 0 || byte > 255) {
+            error('each part must be in the range of `0..255`');
+          }
+          return byte;
+        })
+        .toList();
+  }
+
+  /**
+   * Parse the [host] as an IP version 6 (IPv6) address, returning the address
+   * as a list of 16 bytes in network byte order (big endian).
+   *
+   * Throws a [FormatException] if [host] is not a valid IPv6 address
+   * representation.
+   *
+   * Acts on the substring from [start] to [end]. If [end] is omitted, it
+   * defaults ot the end of the string.
+   *
+   * Some examples of IPv6 addresses:
+   *  * ::1
+   *  * FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
+   *  * 3ffe:2a00:100:7031::1
+   *  * ::FFFF:129.144.52.38
+   *  * 2010:836B:4179::836B:4179
+   */
+  static List<int> parseIPv6Address(String host, [int start = 0, int end]) {
+    if (end == null) end = host.length;
+    // An IPv6 address consists of exactly 8 parts of 1-4 hex digits, seperated
+    // by `:`'s, with the following exceptions:
+    //
+    //  - One (and only one) wildcard (`::`) may be present, representing a fill
+    //    of 0's. The IPv6 `::` is thus 16 bytes of `0`.
+    //  - The last two parts may be replaced by an IPv4 address.
+    void error(String msg, [position]) {
+      throw new FormatException('Illegal IPv6 address, $msg', host, position);
+    }
+    int parseHex(int start, int end) {
+      if (end - start > 4) {
+        error('an IPv6 part can only contain a maximum of 4 hex digits', start);
+      }
+      int value = int.parse(host.substring(start, end), radix: 16);
+      if (value < 0 || value > (1 << 16) - 1) {
+        error('each part must be in the range of `0x0..0xFFFF`', start);
+      }
+      return value;
+    }
+    if (host.length < 2) error('address is too short');
+    List<int> parts = [];
+    bool wildcardSeen = false;
+    int partStart = start;
+    // Parse all parts, except a potential last one.
+    for (int i = start; i < end; i++) {
+      if (host.codeUnitAt(i) == _COLON) {
+        if (i == start) {
+          // If we see a `:` in the beginning, expect wildcard.
+          i++;
+          if (host.codeUnitAt(i) != _COLON) {
+            error('invalid start colon.', i);
+          }
+          partStart = i;
+        }
+        if (i == partStart) {
+          // Wildcard. We only allow one.
+          if (wildcardSeen) {
+            error('only one wildcard `::` is allowed', i);
+          }
+          wildcardSeen = true;
+          parts.add(-1);
+        } else {
+          // Found a single colon. Parse [partStart..i] as a hex entry.
+          parts.add(parseHex(partStart, i));
+        }
+        partStart = i + 1;
+      }
+    }
+    if (parts.length == 0) error('too few parts');
+    bool atEnd = (partStart == end);
+    bool isLastWildcard = (parts.last == -1);
+    if (atEnd && !isLastWildcard) {
+      error('expected a part after last `:`', end);
+    }
+    if (!atEnd) {
+      try {
+        parts.add(parseHex(partStart, end));
+      } catch (e) {
+        // Failed to parse the last chunk as hex. Try IPv4.
+        try {
+          List<int> last = parseIPv4Address(host.substring(partStart, end));
+          parts.add(last[0] << 8 | last[1]);
+          parts.add(last[2] << 8 | last[3]);
+        } catch (e) {
+          error('invalid end of IPv6 address.', partStart);
+        }
+      }
+    }
+    if (wildcardSeen) {
+      if (parts.length > 7) {
+        error('an address with a wildcard must have less than 7 parts');
+      }
+    } else if (parts.length != 8) {
+      error('an address without a wildcard must contain exactly 8 parts');
+    }
+    List<int> bytes = new Uint8List(16);
+    for (int i = 0, index = 0; i < parts.length; i++) {
+      int value = parts[i];
+      if (value == -1) {
+        int wildCardLength = 9 - parts.length;
+        for (int j = 0; j < wildCardLength; j++) {
+          bytes[index] = 0;
+          bytes[index + 1] = 0;
+          index += 2;
+        }
+      } else {
+        bytes[index] = value >> 8;
+        bytes[index + 1] = value & 0xff;
+        index += 2;
+      }
+    }
+    return bytes;
+  }
+
+  // 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;
+  static const int _DOT = 0x2E;
+  static const int _SLASH = 0x2F;
+  static const int _ZERO = 0x30;
+  static const int _NINE = 0x39;
+  static const int _COLON = 0x3A;
+  static const int _LESS = 0x3C;
+  static const int _GREATER = 0x3E;
+  static const int _QUESTION = 0x3F;
+  static const int _AT_SIGN = 0x40;
+  static const int _UPPER_CASE_A = 0x41;
+  static const int _UPPER_CASE_F = 0x46;
+  static const int _UPPER_CASE_Z = 0x5A;
+  static const int _LEFT_BRACKET = 0x5B;
+  static const int _BACKSLASH = 0x5C;
+  static const int _RIGHT_BRACKET = 0x5D;
+  static const int _LOWER_CASE_A = 0x61;
+  static const int _LOWER_CASE_F = 0x66;
+  static const int _LOWER_CASE_Z = 0x7A;
+  static const int _BAR = 0x7C;
+
+  static const String _hexDigits = "0123456789ABCDEF";
+
   external static String _uriEncode(List<int> canonicalTable,
                                     String text,
                                     Encoding encoding,
@@ -3189,13 +2941,13 @@
         throw new ArgumentError.value(mimeType, "mimeType",
                                       "Invalid MIME type");
       }
-      buffer.write(_Uri._uriEncode(_tokenCharTable,
-                                   mimeType.substring(0, slashIndex),
-                                   UTF8, false));
+      buffer.write(Uri._uriEncode(_tokenCharTable,
+                                  mimeType.substring(0, slashIndex),
+                                  UTF8, false));
       buffer.write("/");
-      buffer.write(_Uri._uriEncode(_tokenCharTable,
-                                   mimeType.substring(slashIndex + 1),
-                                   UTF8, false));
+      buffer.write(Uri._uriEncode(_tokenCharTable,
+                                  mimeType.substring(slashIndex + 1),
+                                  UTF8, false));
     }
     if (charsetName != null) {
       if (indices != null) {
@@ -3203,7 +2955,7 @@
                ..add(buffer.length + 8);
       }
       buffer.write(";charset=");
-      buffer.write(_Uri._uriEncode(_tokenCharTable, charsetName, UTF8, false));
+      buffer.write(Uri._uriEncode(_tokenCharTable, charsetName, UTF8, false));
     }
     parameters?.forEach((var key, var value) {
       if (key.isEmpty) {
@@ -3216,10 +2968,10 @@
       if (indices != null) indices.add(buffer.length);
       buffer.write(';');
       // Encode any non-RFC2045-token character and both '%' and '#'.
-      buffer.write(_Uri._uriEncode(_tokenCharTable, key, UTF8, false));
+      buffer.write(Uri._uriEncode(_tokenCharTable, key, UTF8, false));
       if (indices != null) indices.add(buffer.length);
       buffer.write('=');
-      buffer.write(_Uri._uriEncode(_tokenCharTable, value, UTF8, false));
+      buffer.write(Uri._uriEncode(_tokenCharTable, value, UTF8, false));
     });
   }
 
@@ -3236,7 +2988,7 @@
     int slashIndex = -1;
     for (int i = 0; i < mimeType.length; i++) {
       var char = mimeType.codeUnitAt(i);
-      if (char != _SLASH) continue;
+      if (char != Uri._SLASH) continue;
       if (slashIndex < 0) {
         slashIndex = i;
         continue;
@@ -3256,7 +3008,7 @@
    * ````
    *
    * where `type`, `subtype`, `attribute` and `value` are specified in RFC-2045,
-   * and `data` is a sequence of URI-characters (RFC-2396 `uric`).
+   * and `data` is a sequnce of URI-characters (RFC-2396 `uric`).
    *
    * This means that all the characters must be ASCII, but the URI may contain
    * percent-escapes for non-ASCII byte values that need an interpretation
@@ -3267,22 +3019,13 @@
    * and `,` delimiters.
    *
    * Accessing the individual parts may fail later if they turn out to have
-   * content that can't be decoded successfully as a string.
+   * content that can't be decoded sucessfully as a string.
    */
   static UriData parse(String uri) {
-    if (uri.length >= 5) {
-      int dataDelta = _startsWithData(uri, 0);
-      if (dataDelta == 0) {
-        // Exact match on "data:".
-        return _parse(uri, 5, null);
-      }
-      if (dataDelta == 0x20) {
-        // Starts with a non-normalized "data" scheme containing upper-case
-        // letters. Parse anyway, but throw away the scheme.
-        return _parse(uri.substring(5), 0, null);
-      }
+    if (!uri.startsWith("data:")) {
+      throw new FormatException("Does not start with 'data:'", uri, 0);
     }
-    throw new FormatException("Does not start with 'data:'", uri, 0);
+    return _parse(uri, 5, null);
   }
 
   /**
@@ -3307,7 +3050,7 @@
     // That's perfectly reasonable - data URIs are not hierarchical,
     // but it may make some consumers stumble.
     // Should we at least do escape normalization?
-    _uriCache = new _Uri._internal("data", "", null, null, path, query, null);
+    _uriCache = new Uri._internal("data", "", null, null, path, query, null);
     return _uriCache;
   }
 
@@ -3332,7 +3075,7 @@
     int start = _separatorIndices[0] + 1;
     int end = _separatorIndices[1];
     if (start == end) return "text/plain";
-    return _Uri._uriDecode(_text, start, end, UTF8, false);
+    return Uri._uriDecode(_text, start, end, UTF8, false);
   }
 
   /**
@@ -3353,8 +3096,8 @@
       var keyStart = _separatorIndices[i] + 1;
       var keyEnd = _separatorIndices[i + 1];
       if (keyEnd == keyStart + 7 && _text.startsWith("charset", keyStart)) {
-        return _Uri._uriDecode(_text, keyEnd + 1, _separatorIndices[i + 2],
-                               UTF8, false);
+        return Uri._uriDecode(_text, keyEnd + 1, _separatorIndices[i + 2],
+                              UTF8, false);
       }
     }
     return "US-ASCII";
@@ -3412,8 +3155,8 @@
         result[index++] = codeUnit;
       } else {
         if (i + 2 < text.length) {
-          var digit1 = _Uri._parseHexDigit(text.codeUnitAt(i + 1));
-          var digit2 = _Uri._parseHexDigit(text.codeUnitAt(i + 2));
+          var digit1 = Uri._parseHexDigit(text.codeUnitAt(i + 1));
+          var digit2 = Uri._parseHexDigit(text.codeUnitAt(i + 2));
           if (digit1 >= 0 && digit2 >= 0) {
             int byte = digit1 * 16 + digit2;
             result[index++] = byte;
@@ -3434,7 +3177,7 @@
    * If the content is Base64 encoded, it will be decoded to bytes and then
    * decoded to a string using [encoding].
    * If encoding is omitted, the value of a `charset` parameter is used
-   * if it is recognized by [Encoding.getByName], otherwise it defaults to
+   * if it is recongized by [Encoding.getByName], otherwise it defaults to
    * the [ASCII] encoding, which is the default encoding for data URIs
    * that do not specify an encoding.
    *
@@ -3456,7 +3199,7 @@
       var converter = BASE64.decoder.fuse(encoding.decoder);
       return converter.convert(text.substring(start));
     }
-    return _Uri._uriDecode(text, start, text.length, encoding, false);
+    return Uri._uriDecode(text, start, text.length, encoding, false);
   }
 
   /**
@@ -3479,8 +3222,8 @@
       var start = _separatorIndices[i - 2] + 1;
       var equals = _separatorIndices[i - 1];
       var end = _separatorIndices[i];
-      String key = _Uri._uriDecode(_text, start, equals, UTF8, false);
-      String value = _Uri._uriDecode(_text,equals + 1, end, UTF8, false);
+      String key = Uri._uriDecode(_text, start, equals, UTF8, false);
+      String value = Uri._uriDecode(_text,equals + 1, end, UTF8, false);
       result[key] = value;
     }
     return result;
@@ -3563,9 +3306,9 @@
           ((canonicalTable[byte >> 4] & (1 << (byte & 0x0f))) != 0)) {
         buffer.writeCharCode(byte);
       } else {
-        buffer.writeCharCode(_PERCENT);
-        buffer.writeCharCode(_hexDigits.codeUnitAt(byte >> 4));
-        buffer.writeCharCode(_hexDigits.codeUnitAt(byte & 0x0f));
+        buffer.writeCharCode(Uri._PERCENT);
+        buffer.writeCharCode(Uri._hexDigits.codeUnitAt(byte >> 4));
+        buffer.writeCharCode(Uri._hexDigits.codeUnitAt(byte & 0x0f));
       }
     }
     if ((byteOr & ~0xFF) != 0) {
@@ -3614,852 +3357,5 @@
   //  mark        =  "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
   //
   // This is the same characters as in a URI query (which is URI pchar plus '?')
-  static const _uricTable = _Uri._queryCharTable;
+  static const _uricTable = Uri._queryCharTable;
 }
-
-// --------------------------------------------------------------------
-// Constants used to read the scanner result.
-// The indices points into the table filled by [_scan] which contains
-// recognized positions in the scanned URI.
-// The `0` index is only used internally.
-
-/// Index of the position of that `:` after a scheme.
-const int _schemeEndIndex     = 1;
-/// Index of the position of the character just before the host name.
-const int _hostStartIndex     = 2;
-/// Index of the position of the `:` before a port value.
-const int _portStartIndex     = 3;
-/// Index of the position of the first character of a path.
-const int _pathStartIndex     = 4;
-/// Index of the position of the `?` before a query.
-const int _queryStartIndex    = 5;
-/// Index of the position of the `#` before a fragment.
-const int _fragmentStartIndex = 6;
-/// Index of a position where the URI was determined to be "non-simple".
-const int _notSimpleIndex     = 7;
-
-// Initial state for scanner.
-const int _uriStart           = 00;
-
-// If scanning of a URI terminates in this state or above,
-// consider the URI non-simple
-const int _nonSimpleEndStates = 14;
-
-// Initial state for scheme validation.
-const int _schemeStart        = 20;
-
-/// Transition tables used to scan a URI to determine its structure.
-///
-/// The tables represent a state machine with output.
-///
-/// To scan the URI, start in the [_uriStart] state, then read each character
-/// of the URI in order, from start to end, and for each character perform a
-/// transition to a new state while writing the current position into the output
-/// buffer at a designated index.
-///
-/// Each state, represented by an integer which is an index into
-/// [_scannerTables], has a set of transitions, one for each character.
-/// The transitions are encoded as a 5-bit integer representing the next state
-/// and a 3-bit index into the output table.
-///
-/// For URI scanning, only characters in the range U+0020 through U+007E are
-/// interesting, all characters outside that range are treated the same.
-/// The tables only contain 96 entries, representing that characters in the
-/// interesting range, plus one more to represent all values outside the range.
-/// The character entries are stored in one `Uint8List` per state, with the
-/// transition for a character at position `character ^ 0x60`,
-/// which maps the range U+0020 .. U+007F into positions 0 .. 95.
-/// All remaining characters are mapped to position 31 (`0x7f ^ 0x60`) which
-/// represents the transition for all remaining characters.
-final List<Uint8List> _scannerTables = _createTables();
-
-// ----------------------------------------------------------------------
-// Code to create the URI scanner table.
-
-/// Creates the tables for [_scannerTables] used by [Uri.parse].
-///
-/// See [_scannerTables] for the generated format.
-///
-/// The concrete tables are chosen as a trade-off between the number of states
-/// needed and the precision of the result.
-/// This allows definitely recognizing the general structure of the URI
-/// (presence and location of scheme, user-info, host, port, path, query and
-/// fragment) while at the same time detecting that some components are not
-/// in canonical form (anything containing a `%`, a host-name containing a
-/// capital letter). Since the scanner doesn't know whether something is a
-/// scheme or a path until it sees `:`, or user-info or host until it sees
-/// a `@`, a second pass is needed to validate the scheme and any user-info
-/// is considered non-canonical by default.
-///
-/// The states (starting from [_uriStart]) write positions while scanning
-/// a string from `start` to `end` as follows:
-///
-/// - [_schemeEndIndex]: Should be initialized to `start-1`.
-///   If the URI has a scheme, it is set to the position of the `:` after
-///   the scheme.
-/// - [_hostStartIndex]: Should be initialized to `start - 1`.
-///   If the URI has an authority, it is set to the character before the
-///   host name - either the second `/` in the `//` leading the authority,
-///   or the `@` after a user-info. Comparing this value to the scheme end
-///   position can be used to detect that there is a user-info component.
-/// - [_portStartIndex]: Should be initialized to `start`.
-///   Set to the position of the last `:` in an authority, and unchanged
-///   if there is no authority or no `:` in an authority.
-///   If this position is after the host start, there is a port, otherwise it
-///   is just marking a colon in the user-info component.
-/// - [_pathStartIndex]: Should be initialized to `start`.
-///   Is set to the first path character unless the path is empty.
-///   If the path is empty, the position is either unchanged (`start`) or
-///   the first slash of an authority. So, if the path start is before a
-///   host start or scheme end, the path is empty.
-/// - [_queryStartIndex]: Should be initialized to `end`.
-///   The position of the `?` leading a query if the URI contains a query.
-/// - [_fragmentStartIndex]: Should be initialized to `end`.
-///   The position of the `#` leading a fragment if the URI contains a fragment.
-/// - [_notSimpleIndex]: Should be initialized to `start - 1`.
-///   Set to another value if the URI is considered "not simple".
-///   This is elaborated below.
-///
-/// # Simple URIs
-/// A URI is considered "simple" if it is in a normalized form containing no
-/// escapes. This allows us to skip normalization and checking whether escapes
-/// are valid, and to extract components without worrying about unescaping.
-///
-/// The scanner computes a conservative approximation of being "simple".
-/// It rejects any URI with an escape, with a user-info component (mainly
-/// because they are rare and would increase the number of states in the
-/// scanner significantly), with an IPV6 host or with a capital letter in
-/// the scheme or host name (the scheme is handled in a second scan using
-/// a separate two-state table).
-/// Further, paths containing `..` or `.` path segments are considered
-/// non-simple except for pure relative paths (no scheme or authority) starting
-/// with a sequence of "../" segments.
-///
-/// The transition tables cannot detect a trailing ".." in the path,
-/// followed by a query or fragment, because the segment is not known to be
-/// complete until we are past it, and we then need to store the query/fragment
-/// start instead. This cast is checked manually post-scanning (such a path
-/// needs to be normalized to end in "../", so the URI shouldn't be considered
-/// simple).
-List<Uint8List> _createTables() {
-  // TODO(lrn): Use a precomputed table.
-
-  // Total number of states for the scanner.
-  const int stateCount         = 22;
-
-  // States used to scan a URI from scratch.
-  const int schemeOrPath       = 01;
-  const int authOrPath         = 02;
-  const int authOrPathSlash    = 03;
-  const int uinfoOrHost0       = 04;
-  const int uinfoOrHost        = 05;
-  const int uinfoOrPort0       = 06;
-  const int uinfoOrPort        = 07;
-  const int ipv6Host           = 08;
-  const int relPathSeg         = 09;
-  const int pathSeg            = 10;
-  const int path               = 11;
-  const int query              = 12;
-  const int fragment           = 13;
-  const int schemeOrPathDot    = 14;
-  const int schemeOrPathDot2   = 15;
-  const int relPathSegDot      = 16;
-  const int relPathSegDot2     = 17;
-  const int pathSegDot         = 18;
-  const int pathSegDot2        = 19;
-
-  // States used to validate a scheme after its end position has been found.
-  const int scheme0            = _schemeStart;
-  const int scheme             = 21;
-
-  // Constants encoding the write-index for the state transition into the top 5
-  // bits of a byte.
-  const int schemeEnd          = _schemeEndIndex     << 5;
-  const int hostStart          = _hostStartIndex     << 5;
-  const int portStart          = _portStartIndex     << 5;
-  const int pathStart          = _pathStartIndex     << 5;
-  const int queryStart         = _queryStartIndex    << 5;
-  const int fragmentStart      = _fragmentStartIndex << 5;
-  const int notSimple          = _notSimpleIndex     << 5;
-
-  /// The `unreserved` characters of RFC 3986.
-  const unreserved =
-      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"  ;
-  /// The `sub-delim` characters of RFC 3986.
-  const subDelims = r"!$&'()*+,;=";
-  // The `pchar` characters of RFC 3986: characters that may occur in a path,
-  // excluding escapes.
-  const pchar = "$unreserved$subDelims";
-
-  var tables = new List<Uint8List>.generate(stateCount,
-      (_) => new Uint8List(96));
-
-  // Helper function which initialize the table for [state] with a default
-  // transition and returns the table.
-  Uint8List build(state, defaultTransition) =>
-      tables[state]..fillRange(0, 96, defaultTransition);
-
-  // Helper function which sets the transition for each character in [chars]
-  // to [transition] in the [target] table.
-  // The [chars] string must contain only characters in the U+0020 .. U+007E
-  // range.
-  void setChars(Uint8List target, String chars, int transition) {
-    for (int i = 0; i < chars.length; i++) {
-      var char = chars.codeUnitAt(i);
-      target[char ^ 0x60] = transition;
-    }
-  }
-
-  /// Helper function which sets the transition for all characters in the
-  /// range from `range[0]` to `range[1]` to [transition] in the [target] table.
-  ///
-  /// The [range] must be a two-character string where both characters are in
-  /// the U+0020 .. U+007E range and the former character must have a lower
-  /// code point than the latter.
-  void setRange(Uint8List target, String range, int transition) {
-    for (int i = range.codeUnitAt(0), n = range.codeUnitAt(1); i <= n; i++) {
-      target[i ^ 0x60] = transition;
-    }
-  }
-
-  // Create the transitions for each state.
-  var b;
-
-  // Validate as path, if it is a scheme, we handle it later.
-  b = build(_uriStart, schemeOrPath | notSimple);
-  setChars(b, pchar, schemeOrPath);
-  setChars(b, ".", schemeOrPathDot);
-  setChars(b, ":", authOrPath | schemeEnd);  // Handle later.
-  setChars(b, "/", authOrPathSlash);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(schemeOrPathDot, schemeOrPath | notSimple);
-  setChars(b, pchar, schemeOrPath);
-  setChars(b, ".", schemeOrPathDot2);
-  setChars(b, ':', authOrPath | schemeEnd);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(schemeOrPathDot2, schemeOrPath | notSimple);
-  setChars(b, pchar, schemeOrPath);
-  setChars(b, "%", schemeOrPath | notSimple);
-  setChars(b, ':', authOrPath | schemeEnd);
-  setChars(b, "/", relPathSeg);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(schemeOrPath, schemeOrPath | notSimple);
-  setChars(b, pchar, schemeOrPath);
-  setChars(b, ':', authOrPath | schemeEnd);
-  setChars(b, "/", pathSeg);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(authOrPath, path | notSimple);
-  setChars(b, pchar, path | pathStart);
-  setChars(b, "/", authOrPathSlash | pathStart);
-  setChars(b, ".", pathSegDot | pathStart);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(authOrPathSlash, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, "/", uinfoOrHost0 | hostStart);
-  setChars(b, ".", pathSegDot);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(uinfoOrHost0, uinfoOrHost | notSimple);
-  setChars(b, pchar, uinfoOrHost);
-  setRange(b, "AZ", uinfoOrHost | notSimple);
-  setChars(b, ":", uinfoOrPort0 | portStart);
-  setChars(b, "@", uinfoOrHost0 | hostStart);
-  setChars(b, "[", ipv6Host | notSimple);
-  setChars(b, "/", pathSeg | pathStart);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(uinfoOrHost, uinfoOrHost | notSimple);
-  setChars(b, pchar, uinfoOrHost);
-  setRange(b, "AZ", uinfoOrHost | notSimple);
-  setChars(b, ":", uinfoOrPort0 | portStart);
-  setChars(b, "@", uinfoOrHost0 | hostStart);
-  setChars(b, "/", pathSeg | pathStart);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(uinfoOrPort0, uinfoOrPort | notSimple);
-  setRange(b, "19", uinfoOrPort);
-  setChars(b, "@", uinfoOrHost0 | hostStart);
-  setChars(b, "/", pathSeg | pathStart);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(uinfoOrPort, uinfoOrPort | notSimple);
-  setRange(b, "09", uinfoOrPort);
-  setChars(b, "@", uinfoOrHost0 | hostStart);
-  setChars(b, "/", pathSeg | pathStart);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(ipv6Host, ipv6Host);
-  setChars(b, "]", uinfoOrHost);
-
-  b = build(relPathSeg, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, ".", relPathSegDot);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(relPathSegDot, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, ".", relPathSegDot2);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(relPathSegDot2, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, "/", relPathSeg);
-  setChars(b, "?", query | queryStart);  // This should be non-simple.
-  setChars(b, "#", fragment | fragmentStart);  // This should be non-simple.
-
-  b = build(pathSeg, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, ".", pathSegDot);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(pathSegDot, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, ".", pathSegDot2);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(pathSegDot2, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, "/", pathSeg | notSimple);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(path, path | notSimple);
-  setChars(b, pchar, path);
-  setChars(b, "/", pathSeg);
-  setChars(b, "?", query | queryStart);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(query, query | notSimple);
-  setChars(b, pchar, query);
-  setChars(b, "?", query);
-  setChars(b, "#", fragment | fragmentStart);
-
-  b = build(fragment, fragment | notSimple);
-  setChars(b, pchar, fragment);
-  setChars(b, "?", fragment);
-
-  // A separate two-state validator for lower-case scheme names.
-  // Any non-scheme character or upper-case letter is marked as non-simple.
-  b = build(scheme0, scheme | notSimple);
-  setRange(b, "az", scheme);
-
-  b = build(scheme, scheme | notSimple);
-  setRange(b, "az", scheme);
-  setRange(b, "09", scheme);
-  setChars(b, "+-.", scheme);
-
-  return tables;
-}
-
-// --------------------------------------------------------------------
-// Code that uses the URI scanner table.
-
-/// Scan a string using the [_scannerTables] state machine.
-///
-/// Scans [uri] from [start] to [end], startig in state [state] and
-/// writing output into [indices].
-///
-/// Returns the final state.
-int _scan(String uri, int start, int end, int state, List<int> indices) {
-  var tables = _scannerTables;
-  assert(end <= uri.length);
-  for (int i = start; i < end; i++) {
-    var table = tables[state];
-    // Xor with 0x60 to move range 0x20-0x7f into 0x00-0x5f
-    int char = uri.codeUnitAt(i) ^ 0x60;
-    // Use 0x1f (nee 0x7f) to represent all unhandled characters.
-    if (char > 0x5f) char = 0x1f;
-    int transition = table[char];
-    state = transition & 0x1f;
-    indices[transition >> 5] = i;
-  }
-  return state;
-}
-
-class _SimpleUri implements Uri {
-  final String _uri;
-  final int _schemeEnd;
-  final int _hostStart;
-  final int _portStart;
-  final int _pathStart;
-  final int _queryStart;
-  final int _fragmentStart;
-  /// The scheme is often used to distinguish URIs.
-  /// To make comparisons more efficient, we cache the value, and
-  /// canonicalize a few known types.
-  String _schemeCache;
-  int _hashCodeCache;
-
-  _SimpleUri(
-      this._uri,
-      this._schemeEnd,
-      this._hostStart,
-      this._portStart,
-      this._pathStart,
-      this._queryStart,
-      this._fragmentStart,
-      this._schemeCache);
-
-  bool get hasScheme => _schemeEnd > 0;
-  bool get hasAuthority => _hostStart > 0;
-  bool get hasUserInfo => _hostStart > _schemeEnd + 4;
-  bool get hasPort => _hostStart > 0 && _portStart + 1 < _pathStart;
-  bool get hasQuery => _queryStart < _fragmentStart;
-  bool get hasFragment => _fragmentStart < _uri.length;
-
-  bool get _isFile => _schemeEnd == 4 && _uri.startsWith("file");
-  bool get _isHttp => _schemeEnd == 4 && _uri.startsWith("http");
-  bool get _isHttps => _schemeEnd == 5 && _uri.startsWith("https");
-  bool get _isPackage => _schemeEnd == 7 && _uri.startsWith("package");
-  bool _isScheme(String scheme) =>
-    _schemeEnd == scheme.length && _uri.startsWith(scheme);
-
-  bool get hasAbsolutePath => _uri.startsWith("/", _pathStart);
-  bool get hasEmptyPath => _pathStart == _queryStart;
-
-  bool get isAbsolute => hasScheme && !hasFragment;
-
-  String get scheme {
-    if (_schemeEnd <= 0) return "";
-    if (_schemeCache != null) return _schemeCache;
-    if (_isHttp) {
-      _schemeCache = "http";
-    } else if (_isHttps) {
-      _schemeCache = "https";
-    } else if (_isFile) {
-      _schemeCache = "file";
-    } else if (_isPackage) {
-      _schemeCache = "package";
-    } else {
-      _schemeCache = _uri.substring(0, _schemeEnd);
-    }
-    return _schemeCache;
-  }
-  String get authority => _hostStart > 0 ?
-      _uri.substring(_schemeEnd + 3, _pathStart) : "";
-  String get userInfo => (_hostStart > _schemeEnd + 3) ?
-      _uri.substring(_schemeEnd + 3, _hostStart - 1) : "";
-  String get host =>
-      _hostStart > 0 ? _uri.substring(_hostStart, _portStart) : "";
-  int get port {
-    if (hasPort) return int.parse(_uri.substring(_portStart + 1, _pathStart));
-    if (_isHttp) return 80;
-    if (_isHttps) return 443;
-    return 0;
-  }
-  String get path =>_uri.substring(_pathStart, _queryStart);
-  String get query => (_queryStart < _fragmentStart) ?
-     _uri.substring(_queryStart + 1, _fragmentStart) : "";
-  String get fragment => (_fragmentStart < _uri.length) ?
-     _uri.substring(_fragmentStart + 1) : "";
-
-  String get origin {
-    // Check original behavior - W3C spec is wonky!
-    bool isHttp = _isHttp;
-    if (_schemeEnd < 0 || _hostStart == _portStart) {
-      throw new StateError("Cannot use origin without a scheme: $this");
-    }
-    if (!isHttp && !_isHttps) {
-      throw new StateError(
-        "Origin is only applicable schemes http and https: $this");
-    }
-    if (_hostStart == _schemeEnd + 3) {
-      return _uri.substring(0, _pathStart);
-    }
-    // Need to drop anon-empty userInfo.
-    return _uri.substring(0, _schemeEnd + 3) +
-           _uri.substring(_hostStart, _pathStart);
-  }
-
-  List<String> get pathSegments {
-    int start = _pathStart;
-    int end = _queryStart;
-    if (_uri.startsWith("/", start)) start++;
-    if (start == end) return const <String>[];
-    List<String> parts = [];
-    for (int i = start; i < end; i++) {
-      var char = _uri.codeUnitAt(i);
-      if (char == _SLASH) {
-        parts.add(_uri.substring(start, i));
-        start = i + 1;
-      }
-    }
-    parts.add(_uri.substring(start, end));
-    return new List<String>.unmodifiable(parts);
-  }
-
-  Map<String, String> get queryParameters {
-    if (!hasQuery) return const <String, String>{};
-    return new UnmodifiableMapView<String, String>(
-        Uri.splitQueryString(query));
-  }
-
-  Map<String, List<String>> get queryParametersAll {
-    if (!hasQuery) return const <String, List<String>>{};
-    Map queryParameterLists = _Uri._splitQueryStringAll(query);
-    for (var key in queryParameterLists.keys) {
-      queryParameterLists[key] =
-          new List<String>.unmodifiable(queryParameterLists[key]);
-    }
-    return new Map<String, List<String>>.unmodifiable(queryParameterLists);
-  }
-
-  bool _isPort(String port) {
-    int portDigitStart = _portStart + 1;
-    return portDigitStart + port.length == _pathStart &&
-           _uri.startsWith(port, portDigitStart);
-  }
-
-  Uri normalizePath() => this;
-
-  Uri removeFragment() {
-    if (!hasFragment) return this;
-    return new _SimpleUri(
-      _uri.substring(0, _fragmentStart),
-      _schemeEnd, _hostStart, _portStart,
-      _pathStart, _queryStart, _fragmentStart, _schemeCache);
-  }
-
-  Uri replace({String scheme,
-               String userInfo,
-               String host,
-               int port,
-               String path,
-               Iterable<String> pathSegments,
-               String query,
-               Map<String, dynamic/*String|Iterable<String>*/> queryParameters,
-               String fragment}) {
-    bool schemeChanged = false;
-    if (scheme != null) {
-      scheme = _Uri._makeScheme(scheme, 0, scheme.length);
-      schemeChanged = !_isScheme(scheme);
-    } else {
-      scheme = this.scheme;
-    }
-    bool isFile = (scheme == "file");
-    if (userInfo != null) {
-      userInfo = _Uri._makeUserInfo(userInfo, 0, userInfo.length);
-    } else if (_hostStart > 0) {
-      userInfo = _uri.substring(_schemeEnd + 3, _hostStart);
-    } else {
-      userInfo = "";
-    }
-    if (port != null) {
-      port = _Uri._makePort(port, scheme);
-    } else {
-      port = this.hasPort ? this.port : null;
-      if (schemeChanged) {
-        // The default port might have changed.
-        port = _Uri._makePort(port, scheme);
-      }
-    }
-    if (host != null) {
-      host = _Uri._makeHost(host, 0, host.length, false);
-    } else if (_hostStart > 0) {
-      host = _uri.substring(_hostStart, _portStart);
-    } else if (userInfo.isNotEmpty || port != null || isFile) {
-      host = "";
-    }
-
-    bool hasAuthority = host != null;
-    if (path != null || pathSegments != null) {
-      path = _Uri._makePath(path, 0, _stringOrNullLength(path), pathSegments,
-                           scheme, hasAuthority);
-    } else {
-      path = _uri.substring(_pathStart, _queryStart);
-      if ((isFile || (hasAuthority && !path.isEmpty)) &&
-          !path.startsWith('/')) {
-        path = "/" + path;
-      }
-    }
-
-    if (query != null || queryParameters != null) {
-      query = _Uri._makeQuery(
-          query, 0, _stringOrNullLength(query), queryParameters);
-    } else if (_queryStart < _fragmentStart) {
-      query = _uri.substring(_queryStart, _fragmentStart);
-    }
-
-    if (fragment != null) {
-      fragment = _Uri._makeFragment(fragment, 0, fragment.length);
-    } else if (_fragmentStart < _uri.length) {
-      fragment = _uri.substring(_fragmentStart + 1);
-    }
-
-    return new _Uri._internal(
-        scheme, userInfo, host, port, path, query, fragment);
-  }
-
-  Uri resolve(String reference) {
-    return resolveUri(Uri.parse(reference));
-  }
-
-  Uri resolveUri(Uri reference) {
-    if (reference is _SimpleUri) {
-      return _simpleMerge(this, reference);
-    }
-    return _toNonSimple().resolveUri(reference);
-  }
-
-  // Merge two simple URIs. This should always result in a prefix of
-  // one concatentated with a suffix of the other, possibly with a `/` in
-  // the middle of two merged paths, which is again simple.
-  // In a few cases, there might be a need for extra normalization, when
-  // resolving on top of a known scheme.
-  Uri _simpleMerge(_SimpleUri base, _SimpleUri ref) {
-    if (ref.hasScheme) return ref;
-    if (ref.hasAuthority) {
-      if (!base.hasScheme) return ref;
-      bool isSimple = true;
-      if (base._isFile) {
-        isSimple = !ref.hasEmptyPath;
-      } else if (base._isHttp) {
-        isSimple = !ref._isPort("80");
-      } else if (base._isHttps) {
-        isSimple = !ref._isPort("443");
-      }
-      if (isSimple) {
-        var delta = base._schemeEnd + 1;
-        var newUri = base._uri.substring(0, base._schemeEnd + 1) +
-                     ref._uri.substring(ref._schemeEnd + 1);
-        return new _SimpleUri(newUri,
-           base._schemeEnd,
-           ref._hostStart + delta,
-           ref._portStart + delta,
-           ref._pathStart + delta,
-           ref._queryStart + delta,
-           ref._fragmentStart + delta,
-           base._schemeCache);
-      } else {
-        // This will require normalization, so use the _Uri implementation.
-        return _toNonSimple().resolveUri(ref);
-      }
-    }
-    if (ref.hasEmptyPath) {
-      if (ref.hasQuery) {
-        int delta = base._queryStart - ref._queryStart;
-        var newUri = base._uri.substring(0, base._queryStart) +
-                     ref._uri.substring(ref._queryStart);
-        return new _SimpleUri(newUri,
-           base._schemeEnd,
-           base._hostStart,
-           base._portStart,
-           base._pathStart,
-           ref._queryStart + delta,
-           ref._fragmentStart + delta,
-           base._schemeCache);
-      }
-      if (ref.hasFragment) {
-        int delta = base._fragmentStart - ref._fragmentStart;
-        var newUri = base._uri.substring(0, base._fragmentStart) +
-                     ref._uri.substring(ref._fragmentStart);
-        return new _SimpleUri(newUri,
-           base._schemeEnd,
-           base._hostStart,
-           base._portStart,
-           base._pathStart,
-           base._queryStart,
-           ref._fragmentStart + delta,
-           base._schemeCache);
-      }
-      return base.removeFragment();
-    }
-    if (ref.hasAbsolutePath) {
-      var delta = base._pathStart - ref._pathStart;
-      var newUri = base._uri.substring(0, base._pathStart) +
-                   ref._uri.substring(ref._pathStart);
-      return new _SimpleUri(newUri,
-        base._schemeEnd,
-        base._hostStart,
-        base._portStart,
-        base._pathStart,
-        ref._queryStart + delta,
-        ref._fragmentStart + delta,
-        base._schemeCache);
-    }
-    if (base.hasEmptyPath && base.hasAuthority) {
-      // ref has relative non-empty path.
-      // Add a "/" in front, then leading "/../" segments are folded to "/".
-      int refStart = ref._pathStart;
-      while (ref._uri.startsWith("../", refStart)) {
-        refStart += 3;
-      }
-      var delta = base._pathStart - refStart + 1;
-      var newUri = "${base._uri.substring(0, base._pathStart)}/"
-                   "${ref._uri.substring(refStart)}";
-      return new _SimpleUri(newUri,
-        base._schemeEnd,
-        base._hostStart,
-        base._portStart,
-        base._pathStart,
-        ref._queryStart + delta,
-        ref._fragmentStart + delta,
-        base._schemeCache);
-    }
-    // Merge paths.
-    if (base._uri.startsWith("../", base._pathStart)) {
-      // Complex rare case, go slow.
-      return _toNonSimple().resolveUri(ref);
-    }
-
-    // The RFC 3986 algorithm merges the base path without its final segment
-    // (anything after the final "/", or everything if the base path doesn't
-    // contain any "/"), and the reference path.
-    // Then it removes "." and ".." segments using the remove-dot-segment
-    // algorithm.
-    // This code combines the two steps. It is simplified by knowing that
-    // the base path contains no "." or ".." segments, and the reference
-    // path can only contain leading ".." segments.
-
-    String baseUri = base._uri;
-    String refUri = ref._uri;
-    int baseStart = base._pathStart;
-    int baseEnd = base._queryStart;
-    int refStart = ref._pathStart;
-    int refEnd = ref._queryStart;
-    int backCount = 1;
-
-    int slashCount = 0;
-
-    // Count leading ".." segments in reference path.
-    while (refStart + 3 <= refEnd && refUri.startsWith("../", refStart)) {
-      refStart += 3;
-      backCount += 1;
-    }
-
-    // Extra slash inserted between base and reference path parts if
-    // the base path contains any slashes.
-    // (We could use a slash from the base path in most cases, but not if
-    // we remove the entire base path).
-    String insert = "";
-    while (baseEnd > baseStart) {
-      baseEnd--;
-      int char = baseUri.codeUnitAt(baseEnd);
-      if (char == _SLASH) {
-        insert = "/";
-        backCount--;
-        if (backCount == 0) break;
-      }
-    }
-    // If the base URI has no scheme or authority (`_pathStart == 0`)
-    // and a relative path, and we reached the beginning of the path,
-    // we have a special case.
-    if (baseEnd == 0 && !base.hasAbsolutePath) {
-      // Non-RFC 3986 behavior when resolving a purely relative path on top of
-      // another relative path: Don't make the result absolute.
-      insert = "";
-    }
-
-    var delta = baseEnd - refStart + insert.length;
-    var newUri = "${base._uri.substring(0, baseEnd)}$insert"
-               "${ref._uri.substring(refStart)}";
-
-    return new _SimpleUri(newUri,
-      base._schemeEnd,
-      base._hostStart,
-      base._portStart,
-      base._pathStart,
-      ref._queryStart + delta,
-      ref._fragmentStart + delta,
-      base._schemeCache);
-  }
-
-  String toFilePath({bool windows}) {
-    if (_schemeEnd >= 0 && !_isFile) {
-      throw new UnsupportedError(
-          "Cannot extract a file path from a $scheme URI");
-    }
-    if (_queryStart < _uri.length) {
-      if (_queryStart < _fragmentStart) {
-        throw new UnsupportedError(
-            "Cannot extract a file path from a URI with a query component");
-      }
-      throw new UnsupportedError(
-          "Cannot extract a file path from a URI with a fragment component");
-    }
-    if (windows == null) windows = _Uri._isWindows;
-    return windows ? _Uri._toWindowsFilePath(this) : _toFilePath();
-  }
-
-  String _toFilePath() {
-    if (_hostStart < _portStart) {
-      // Has authority and non-empty host.
-      throw new UnsupportedError(
-        "Cannot extract a non-Windows file path from a file URI "
-        "with an authority");
-    }
-    return this.path;
-  }
-
-  UriData get data {
-    assert(scheme != "data");
-    return null;
-  }
-
-  int get hashCode => _hashCodeCache ??= _uri.hashCode;
-
-  bool operator==(Object other) {
-    if (identical(this, other)) return true;
-    if (other is Uri) return _uri == other.toString();
-    return false;
-  }
-
-  Uri _toNonSimple() {
-    return new _Uri._internal(
-      this.scheme,
-      this.userInfo,
-      this.hasAuthority ? this.host: null,
-      this.hasPort ? this.port : null,
-      this.path,
-      this.hasQuery ? this.query : null,
-      this.hasFragment ? this.fragment : null
-    );
-  }
-
-  String toString() => _uri;
-}
-
-/// Checks whether [text] starts with "data:" at position [start].
-///
-/// The text must be long enough to allow reading five characters
-/// from the [start] position.
-///
-/// Returns an integer value which is zero if text starts with all-lowercase
-/// "data:" and 0x20 if the text starts with "data:" that isn't all lower-case.
-/// All other values means the text starts with some other character.
-int _startsWithData(String text, int start) {
-  // Multiply by 3 to avoid a non-colon character making delta be 0x20.
-  int delta = (text.codeUnitAt(start + 4) ^ _COLON) * 3;
-  delta |= text.codeUnitAt(start)     ^ 0x64 /*d*/;
-  delta |= text.codeUnitAt(start + 1) ^ 0x61 /*a*/;
-  delta |= text.codeUnitAt(start + 2) ^ 0x74 /*t*/;
-  delta |= text.codeUnitAt(start + 3) ^ 0x61 /*a*/;
-  return delta;
-}
-
-/// Helper function returning the length of a string, or `0` for `null`.
-int _stringOrNullLength(String s) => (s == null) ? 0 : s.length;
diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart
index 2c1684e..e365f3b 100644
--- a/tests/compiler/dart2js/compiler_helper.dart
+++ b/tests/compiler/dart2js/compiler_helper.dart
@@ -179,7 +179,7 @@
 
 Future compileSources(Map<String, String> sources,
                check(MockCompiler compiler)) {
-  Uri base = new Uri(scheme: 'source', path: '/');
+  Uri base = new Uri(scheme: 'source');
   Uri mainUri = base.resolve('main.dart');
   String mainCode = sources['main.dart'];
   Expect.isNotNull(mainCode, 'No source code found for "main.dart"');
diff --git a/tests/compiler/dart2js/mirrors_used_test.dart b/tests/compiler/dart2js/mirrors_used_test.dart
index 4b9c31a..fc08b6b 100644
--- a/tests/compiler/dart2js/mirrors_used_test.dart
+++ b/tests/compiler/dart2js/mirrors_used_test.dart
@@ -69,7 +69,7 @@
     // 2. Some code was refactored, and there are more methods.
     // Either situation could be problematic, but in situation 2, it is often
     // acceptable to increase [expectedMethodCount] a little.
-    int expectedMethodCount = 466;
+    int expectedMethodCount = 432;
     Expect.isTrue(
         generatedCode.length <= expectedMethodCount,
         'Too many compiled methods: '
diff --git a/tests/corelib/data_uri_test.dart b/tests/corelib/data_uri_test.dart
index 41f2290..1a21be1 100644
--- a/tests/corelib/data_uri_test.dart
+++ b/tests/corelib/data_uri_test.dart
@@ -18,13 +18,6 @@
   testRoundTrip("blåbærgrød", UTF8);
   testRoundTrip("blåbærgrød", LATIN1);
 
-  testUriEquals("data:,abc?d#e");
-  testUriEquals("DATA:,ABC?D#E");
-  testUriEquals("data:,a%20bc?d#e");
-  testUriEquals("DATA:,A%20BC?D#E");
-  testUriEquals("data:,a%62c?d#e");
-  testUriEquals("DATA:,A%42C?D#E");
-
   testUtf8Encoding("\u1000\uffff");
   testBytes();
   testInvalidCharacters();
@@ -257,11 +250,3 @@
   Expect.equals(expect.hasFragment, actual.hasFragment, "hasFragment");
   Expect.equals(expect.fragment, actual.fragment, "fragment");
 }
-
-void testUriEquals(String uriText) {
-  var data = UriData.parse(uriText);
-  var uri = Uri.parse(uriText);
-  Expect.equals(data.uri, uri);
-  Expect.equals(data.toString(), uri.data.toString());
-  Expect.equals(data.toString(), uri.toString());
-}
diff --git a/tests/corelib/uri_test.dart b/tests/corelib/uri_test.dart
index 3d24e2d..ef7a9a3 100644
--- a/tests/corelib/uri_test.dart
+++ b/tests/corelib/uri_test.dart
@@ -10,12 +10,6 @@
 testUri(String uriText, bool isAbsolute) {
   var uri = Uri.parse(uriText);
 
-  // Test that parsing a substring works the same as parsing the string.
-  String wrapper = "://@[]:/%?#";
-  var embeddedUri = Uri.parse(
-       "$wrapper$uri$wrapper", wrapper.length, uriText.length + wrapper.length);
-
-  Expect.equals(uri, embeddedUri);
   Expect.equals(isAbsolute, uri.isAbsolute);
   Expect.stringEquals(uriText, uri.toString());
 
@@ -88,8 +82,7 @@
   final urisSample = "http://a/b/c/d;p?q";
   Uri base = Uri.parse(urisSample);
   testResolve(expect, relative) {
-    String name = "$base << $relative";
-    Expect.stringEquals(expect, base.resolve(relative).toString(), name);
+    Expect.stringEquals(expect, base.resolve(relative).toString());
   }
 
   // From RFC 3986.
@@ -139,232 +132,19 @@
   // Additional tests (not from RFC 3986).
   testResolve("http://a/b/g;p/h;s",    "../g;p/h;s");
 
-  base = Uri.parse("s:a/b");
-  testResolve("s:a/c", "c");
-  testResolve("s:/c", "../c");
-
-  base = Uri.parse("S:a/b");
-  testResolve("s:a/c", "c");
-  testResolve("s:/c", "../c");
-
-  base = Uri.parse("s:foo");
-  testResolve("s:bar", "bar");
-  testResolve("s:bar", "../bar");
-
-  base = Uri.parse("S:foo");
-  testResolve("s:bar", "bar");
-  testResolve("s:bar", "../bar");
-
-  // Special-case (deliberate non-RFC behavior).
-  base = Uri.parse("foo/bar");
-  testResolve("foo/baz", "baz");
-  testResolve("baz", "../baz");
-
-  base = Uri.parse("s:/foo");
-  testResolve("s:/bar", "bar");
-  testResolve("s:/bar", "../bar");
-
-  base = Uri.parse("S:/foo");
-  testResolve("s:/bar", "bar");
-  testResolve("s:/bar", "../bar");
-
   // Test non-URI base (no scheme, no authority, relative path).
   base = Uri.parse("a/b/c?_#_");
   testResolve("a/b/g?q#f", "g?q#f");
   testResolve("./", "../..");
   testResolve("../", "../../..");
   testResolve("a/b/", ".");
-  testResolve("c", "../../c");  // Deliberate non-RFC behavior.
+  testResolve("c", "../../c");
   base = Uri.parse("../../a/b/c?_#_");  // Initial ".." in base url.
   testResolve("../../a/d", "../d");
   testResolve("../../../d", "../../../d");
 
-  base = Uri.parse("s://h/p?q#f");  // A simple base.
-  // Simple references:
-  testResolve("s2://h2/P?Q#F", "s2://h2/P?Q#F");
-  testResolve("s://h2/P?Q#F", "//h2/P?Q#F");
-  testResolve("s://h/P?Q#F", "/P?Q#F");
-  testResolve("s://h/p?Q#F", "?Q#F");
-  testResolve("s://h/p?q#F", "#F");
-  testResolve("s://h/p?q", "");
-  // Non-simple references:
-  testResolve("s2://I@h2/P?Q#F%20", "s2://I@h2/P?Q#F%20");
-  testResolve("s://I@h2/P?Q#F%20", "//I@h2/P?Q#F%20");
-  testResolve("s://h2/P?Q#F%20", "//h2/P?Q#F%20");
-  testResolve("s://h/P?Q#F%20", "/P?Q#F%20");
-  testResolve("s://h/p?Q#F%20", "?Q#F%20");
-  testResolve("s://h/p?q#F%20", "#F%20");
-
-  base = Uri.parse("s://h/p1/p2/p3");  // A simple base with a path.
-  testResolve("s://h/p1/p2/", ".");
-  testResolve("s://h/p1/p2/", "./");
-  testResolve("s://h/p1/", "..");
-  testResolve("s://h/p1/", "../");
-  testResolve("s://h/", "../..");
-  testResolve("s://h/", "../../");
-  testResolve("s://h/p1/%20", "../%20");
-  testResolve("s://h/", "../../../..");
-  testResolve("s://h/", "../../../../");
-
-  base = Uri.parse("s://h/p?q#f%20");  // A non-simpe base.
-  // Simple references:
-  testResolve("s2://h2/P?Q#F", "s2://h2/P?Q#F");
-  testResolve("s://h2/P?Q#F", "//h2/P?Q#F");
-  testResolve("s://h/P?Q#F", "/P?Q#F");
-  testResolve("s://h/p?Q#F", "?Q#F");
-  testResolve("s://h/p?q#F", "#F");
-  testResolve("s://h/p?q", "");
-  // Non-simple references:
-  testResolve("s2://I@h2/P?Q#F%20", "s2://I@h2/P?Q#F%20");
-  testResolve("s://I@h2/P?Q#F%20", "//I@h2/P?Q#F%20");
-  testResolve("s://h2/P?Q#F%20", "//h2/P?Q#F%20");
-  testResolve("s://h/P?Q#F%20", "/P?Q#F%20");
-  testResolve("s://h/p?Q#F%20", "?Q#F%20");
-  testResolve("s://h/p?q#F%20", "#F%20");
-
-  base = Uri.parse("S://h/p1/p2/p3");  // A non-simple base with a path.
-  testResolve("s://h/p1/p2/", ".");
-  testResolve("s://h/p1/p2/", "./");
-  testResolve("s://h/p1/", "..");
-  testResolve("s://h/p1/", "../");
-  testResolve("s://h/", "../..");
-  testResolve("s://h/", "../../");
-  testResolve("s://h/p1/%20", "../%20");
-  testResolve("s://h/", "../../../..");
-  testResolve("s://h/", "../../../../");
-
-  base = Uri.parse("../../../");  // A simple relative path.
-  testResolve("../../../a", "a");
-  testResolve("../../../../a", "../a");
-  testResolve("../../../a%20", "a%20");
-  testResolve("../../../../a%20", "../a%20");
-
-  // Tests covering the branches of the merge algorithm in RFC 3986
-  // with both simple and complex base URIs.
-  for (var b in ["s://a/pa/pb?q#f", "s://a/pa/pb?q#f%20"]) {
-    var origBase = Uri.parse(b);
-    base = origBase;
-
-    // if defined(R.scheme) then ...
-    testResolve("s2://a2/p2?q2#f2", "s2://a2/p2?q2#f2");
-    // else, if defined(R.authority) then ...
-    testResolve("s://a2/p2?q2#f2", "//a2/p2?q2#f2");
-    testResolve("s://a2/?q2#f2", "//a2/../?q2#f2");
-    testResolve("s://a2?q2#f2", "//a2?q2#f2");
-    testResolve("s://a2#f2", "//a2#f2");
-    testResolve("s://a2", "//a2");
-    // else, if (R.path == "") then ...
-    //   if defined(R.query) then
-    testResolve("s://a/pa/pb?q2#f2", "?q2#f2");
-    testResolve("s://a/pa/pb?q2", "?q2");
-    //   else
-    testResolve("s://a/pa/pb?q#f2", "#f2");
-    testResolve("s://a/pa/pb?q", "");
-    // else, if (R.path starts-with "/") then ...
-    testResolve("s://a/p2?q2#f2", "/p2?q2#f2");
-    testResolve("s://a/?q2#f2", "/?q2#f2");
-    testResolve("s://a/#f2", "/#f2");
-    testResolve("s://a/", "/");
-    testResolve("s://a/", "/../");
-    // else ... T.path = merge(Base.path, R.path)
-    // ... remove-dot-fragments(T.path) ...
-    // (Cover the merge function and the remove-dot-fragments functions too).
-
-    // If base has authority and empty path ...
-    var emptyPathBase = Uri.parse(b.replaceFirst("/pa/pb", ""));
-    base = emptyPathBase;
-    testResolve("s://a/p2?q2#f2", "p2?q2#f2");
-    testResolve("s://a/p2#f2", "p2#f2");
-    testResolve("s://a/p2", "p2");
-
-    base = origBase;
-    // otherwise
-    // (Cover both no authority and non-empty path and both).
-    var noAuthEmptyPathBase = Uri.parse(b.replaceFirst("//a/pa/pb", ""));
-    var noAuthAbsPathBase = Uri.parse(b.replaceFirst("//a", ""));
-    var noAuthRelPathBase = Uri.parse(b.replaceFirst("//a/", ""));
-    var noAuthRelSinglePathBase = Uri.parse(b.replaceFirst("//a/pa/", ""));
-
-    testResolve("s://a/pa/p2?q2#f2", "p2?q2#f2");
-    testResolve("s://a/pa/p2#f2", "p2#f2");
-    testResolve("s://a/pa/p2", "p2");
-
-    base = noAuthEmptyPathBase;
-    testResolve("s:p2?q2#f2", "p2?q2#f2");
-    testResolve("s:p2#f2", "p2#f2");
-    testResolve("s:p2", "p2");
-
-    base = noAuthAbsPathBase;
-    testResolve("s:/pa/p2?q2#f2", "p2?q2#f2");
-    testResolve("s:/pa/p2#f2", "p2#f2");
-    testResolve("s:/pa/p2", "p2");
-
-    base = noAuthRelPathBase;
-    testResolve("s:pa/p2?q2#f2", "p2?q2#f2");
-    testResolve("s:pa/p2#f2", "p2#f2");
-    testResolve("s:pa/p2", "p2");
-
-    base = noAuthRelSinglePathBase;
-    testResolve("s:p2?q2#f2", "p2?q2#f2");
-    testResolve("s:p2#f2", "p2#f2");
-    testResolve("s:p2", "p2");
-
-    // Then remove dot segments.
-
-    // A. if input buffer starts with "../" or "./".
-    // This only happens if base has only a single (may be empty) segment and
-    // no slash.
-    base = emptyPathBase;
-    testResolve("s://a/p2", "../p2");
-    testResolve("s://a/", "../");
-    testResolve("s://a/", "..");
-    testResolve("s://a/p2", "./p2");
-    testResolve("s://a/", "./");
-    testResolve("s://a/", ".");
-    testResolve("s://a/p2", "../../p2");
-    testResolve("s://a/p2", "../../././p2");
-
-    base = noAuthRelSinglePathBase;
-    testResolve("s:p2", "../p2");
-    testResolve("s:", "../");
-    testResolve("s:", "..");
-    testResolve("s:p2", "./p2");
-    testResolve("s:", "./");
-    testResolve("s:", ".");
-    testResolve("s:p2", "../../p2");
-    testResolve("s:p2", "../../././p2");
-
-    // B. if input buffer starts with "/./" or is "/.". replace with "/".
-    // (The URI implementation removes the "." path segments when parsing,
-    // so this case isn't handled by merge).
-    base = origBase;
-    testResolve("s://a/pa/p2", "./p2");
-
-    // C. if input buffer starts with "/../" or is "/..", replace with "/"
-    // and remove preceeding segment.
-    testResolve("s://a/p2", "../p2");
-    var longPathBase = Uri.parse(b.replaceFirst("/pb", "/pb/pc/pd"));
-    base = longPathBase;
-    testResolve("s://a/pa/pb/p2", "../p2");
-    testResolve("s://a/pa/p2", "../../p2");
-    testResolve("s://a/p2", "../../../p2");
-    testResolve("s://a/p2", "../../../../p2");
-    var noAuthRelLongPathBase =
-        Uri.parse(b.replaceFirst("//a/pa/pb", "pa/pb/pc/pd"));
-    base = noAuthRelLongPathBase;
-    testResolve("s:pa/pb/p2", "../p2");
-    testResolve("s:pa/p2", "../../p2");
-    testResolve("s:/p2", "../../../p2");
-    testResolve("s:/p2", "../../../../p2");
-
-    // D. if the input buffer contains only ".." or ".", remove it.
-    base = noAuthEmptyPathBase;
-    testResolve("s:", "..");
-    testResolve("s:", ".");
-    base = noAuthRelSinglePathBase;
-    testResolve("s:", "..");
-    testResolve("s:", ".");
-  }
+  base = Uri.parse("s:a/b");
+  testResolve("s:/c", "../c");
 }
 
 void testResolvePath(String expected, String path) {
@@ -718,11 +498,6 @@
                           query: null,
                           fragment: null).toString());
   Expect.stringEquals("file:///", Uri.parse("file:").toString());
-  Expect.stringEquals("file:///", Uri.parse("file:/").toString());
-  Expect.stringEquals("file:///", Uri.parse("file:").toString());
-  Expect.stringEquals("file:///foo", Uri.parse("file:foo").toString());
-  Expect.stringEquals("file:///foo", Uri.parse("file:/foo").toString());
-  Expect.stringEquals("file://foo/", Uri.parse("file://foo").toString());
 
   testResolvePath("/a/g", "/a/b/c/./../../g");
   testResolvePath("/a/g", "/a/b/c/./../../g");