summaryrefslogtreecommitdiff
path: root/ext/url/00_url.js
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-09-10 09:15:16 +0530
committerGitHub <noreply@github.com>2022-09-10 09:15:16 +0530
commita54d5654a26d29dfca1162ccc5476e9224a657e9 (patch)
tree18ef0365e9f3d4e25fffc5f6d2f9be044b7143f1 /ext/url/00_url.js
parent59476ab96d04e92c2f00704672e692fc9d2b12a2 (diff)
perf: optimize URL serialization (#15663)
Diffstat (limited to 'ext/url/00_url.js')
-rw-r--r--ext/url/00_url.js270
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;
}
}