diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-09-10 09:15:16 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-10 09:15:16 +0530 |
commit | a54d5654a26d29dfca1162ccc5476e9224a657e9 (patch) | |
tree | 18ef0365e9f3d4e25fffc5f6d2f9be044b7143f1 /ext/url/00_url.js | |
parent | 59476ab96d04e92c2f00704672e692fc9d2b12a2 (diff) |
perf: optimize URL serialization (#15663)
Diffstat (limited to 'ext/url/00_url.js')
-rw-r--r-- | ext/url/00_url.js | 270 |
1 files changed, 211 insertions, 59 deletions
diff --git a/ext/url/00_url.js b/ext/url/00_url.js index ab87e85e0..659e6a37b 100644 --- a/ext/url/00_url.js +++ b/ext/url/00_url.js @@ -19,9 +19,9 @@ ArrayPrototypeSort, ArrayPrototypeSplice, ObjectKeys, + Uint32Array, SafeArrayIterator, StringPrototypeSlice, - StringPrototypeSplit, Symbol, SymbolFor, SymbolIterator, @@ -44,41 +44,37 @@ // Helper functions function opUrlReparse(href, setter, value) { - return _urlParts( - ops.op_url_reparse(href, [setter, value]), + const status = ops.op_url_reparse( + href, + setter, + value, + componentsBuf.buffer, ); + return getSerialization(status, href); } + function opUrlParse(href, maybeBase) { - return _urlParts(ops.op_url_parse(href, maybeBase)); + let status; + if (maybeBase === undefined) { + status = ops.op_url_parse(href, componentsBuf.buffer); + } else { + status = core.ops.op_url_parse_with_base( + href, + maybeBase, + componentsBuf.buffer, + ); + } + return getSerialization(status, href); } - function _urlParts(internalParts) { - // WARNING: must match UrlParts serialization rust's url_result() - const { - 0: href, - 1: hash, - 2: host, - 3: hostname, - 4: origin, - 5: password, - 6: pathname, - 7: port, - 8: protocol, - 9: search, - 10: username, - } = StringPrototypeSplit(internalParts, "\n"); - return { - href, - hash, - host, - hostname, - origin, - password, - pathname, - port, - protocol, - search, - username, - }; + + function getSerialization(status, href) { + if (status === 0) { + return href; + } else if (status === 1) { + return core.ops.op_url_get_serialization(); + } else { + throw new TypeError("Invalid URL"); + } } class URLSearchParams { @@ -131,7 +127,7 @@ if (url === null) { return; } - url[_url] = opUrlReparse(url.href, SET_SEARCH, this.toString()); + url[_updateUrlSearch](this.toString()); } /** @@ -308,11 +304,37 @@ URLSearchParamsPrototype, ); - const _url = Symbol("url"); + const _updateUrlSearch = Symbol("updateUrlSearch"); + + function trim(s) { + if (s.length === 1) return ""; + return s; + } + + // Represents a "no port" value. A port in URL cannot be greater than 2^16 − 1 + const NO_PORT = 65536; + const componentsBuf = new Uint32Array(8); class URL { - [_url]; #queryObject = null; + #serialization; + #schemeEnd; + #usernameEnd; + #hostStart; + #hostEnd; + #port; + #pathStart; + #queryStart; + #fragmentStart; + + [_updateUrlSearch](value) { + this.#serialization = opUrlReparse( + this.#serialization, + SET_SEARCH, + value, + ); + this.#updateComponents(); + } /** * @param {string} url @@ -328,7 +350,21 @@ }); } this[webidl.brand] = webidl.brand; - this[_url] = opUrlParse(url, base); + this.#serialization = opUrlParse(url, base); + this.#updateComponents(); + } + + #updateComponents() { + [ + this.#schemeEnd, + this.#usernameEnd, + this.#hostStart, + this.#hostEnd, + this.#port, + this.#pathStart, + this.#queryStart, + this.#fragmentStart, + ] = componentsBuf; } [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { @@ -363,10 +399,18 @@ } } + #hasAuthority() { + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L824 + return this.#serialization.slice(this.#schemeEnd).startsWith("://"); + } + /** @return {string} */ get hash() { webidl.assertBranded(this, URLPrototype); - return this[_url].hash; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L263 + return this.#fragmentStart + ? trim(this.#serialization.slice(this.#fragmentStart)) + : ""; } /** @param {string} value */ @@ -379,7 +423,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_HASH, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_HASH, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -388,7 +437,8 @@ /** @return {string} */ get host() { webidl.assertBranded(this, URLPrototype); - return this[_url].host; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L101 + return this.#serialization.slice(this.#hostStart, this.#pathStart); } /** @param {string} value */ @@ -401,7 +451,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_HOST, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_HOST, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -410,7 +465,8 @@ /** @return {string} */ get hostname() { webidl.assertBranded(this, URLPrototype); - return this[_url].hostname; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L988 + return this.#serialization.slice(this.#hostStart, this.#hostEnd); } /** @param {string} value */ @@ -423,7 +479,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_HOSTNAME, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_HOSTNAME, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -432,7 +493,7 @@ /** @return {string} */ get href() { webidl.assertBranded(this, URLPrototype); - return this[_url].href; + return this.#serialization; } /** @param {string} value */ @@ -444,20 +505,50 @@ prefix, context: "Argument 1", }); - this[_url] = opUrlParse(value); + this.#serialization = opUrlParse(value); + this.#updateComponents(); this.#updateSearchParams(); } /** @return {string} */ get origin() { webidl.assertBranded(this, URLPrototype); - return this[_url].origin; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/origin.rs#L14 + const scheme = this.#serialization.slice(0, this.#schemeEnd); + if ( + scheme === "http" || scheme === "https" || scheme === "ftp" || + scheme === "ws" || scheme === "wss" + ) { + return `${scheme}://${this.host}`; + } + + if (scheme === "blob") { + // TODO(@littledivy): Fast path. + try { + return new URL(this.pathname).origin; + } catch { + return "null"; + } + } + + return "null"; } /** @return {string} */ get password() { webidl.assertBranded(this, URLPrototype); - return this[_url].password; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L914 + if ( + this.#hasAuthority() && + this.#usernameEnd !== this.#serialization.length && + this.#serialization[this.#usernameEnd] === ":" + ) { + return this.#serialization.slice( + this.#usernameEnd + 1, + this.#hostStart - 1, + ); + } + return ""; } /** @param {string} value */ @@ -470,7 +561,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_PASSWORD, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_PASSWORD, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -479,7 +575,13 @@ /** @return {string} */ get pathname() { webidl.assertBranded(this, URLPrototype); - return this[_url].pathname; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L1203 + if (!this.#queryStart && !this.#fragmentStart) { + return this.#serialization.slice(this.#pathStart); + } + + const nextComponentStart = this.#queryStart || this.#fragmentStart; + return this.#serialization.slice(this.#pathStart, nextComponentStart); } /** @param {string} value */ @@ -492,7 +594,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_PATHNAME, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_PATHNAME, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -501,7 +608,15 @@ /** @return {string} */ get port() { webidl.assertBranded(this, URLPrototype); - return this[_url].port; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L196 + if (this.#port === NO_PORT) { + return this.#serialization.slice(this.#hostEnd, this.#pathStart); + } else { + return this.#serialization.slice( + this.#hostEnd + 1, /* : */ + this.#pathStart, + ); + } } /** @param {string} value */ @@ -514,7 +629,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_PORT, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_PORT, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -523,7 +643,8 @@ /** @return {string} */ get protocol() { webidl.assertBranded(this, URLPrototype); - return this[_url].protocol; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L56 + return this.#serialization.slice(0, this.#schemeEnd + 1 /* : */); } /** @param {string} value */ @@ -536,7 +657,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_PROTOCOL, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_PROTOCOL, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -545,7 +671,11 @@ /** @return {string} */ get search() { webidl.assertBranded(this, URLPrototype); - return this[_url].search; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L249 + const afterPath = this.#queryStart || this.#fragmentStart || + this.#serialization.length; + const afterQuery = this.#fragmentStart || this.#serialization.length; + return trim(this.#serialization.slice(afterPath, afterQuery)); } /** @param {string} value */ @@ -558,7 +688,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_SEARCH, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_SEARCH, + value, + ); + this.#updateComponents(); this.#updateSearchParams(); } catch { /* pass */ @@ -568,7 +703,19 @@ /** @return {string} */ get username() { webidl.assertBranded(this, URLPrototype); - return this[_url].username; + // https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L881 + const schemeSeperatorLen = 3; /* :// */ + if ( + this.#hasAuthority() && + this.#usernameEnd > this.#schemeEnd + schemeSeperatorLen + ) { + return this.#serialization.slice( + this.#schemeEnd + schemeSeperatorLen, + this.#usernameEnd, + ); + } else { + return ""; + } } /** @param {string} value */ @@ -581,7 +728,12 @@ context: "Argument 1", }); try { - this[_url] = opUrlReparse(this[_url].href, SET_USERNAME, value); + this.#serialization = opUrlReparse( + this.#serialization, + SET_USERNAME, + value, + ); + this.#updateComponents(); } catch { /* pass */ } @@ -599,13 +751,13 @@ /** @return {string} */ toString() { webidl.assertBranded(this, URLPrototype); - return this[_url].href; + return this.#serialization; } /** @return {string} */ toJSON() { webidl.assertBranded(this, URLPrototype); - return this[_url].href; + return this.#serialization; } } |