url: improve performance of the format function

PR-URL: https://github.com/nodejs/node/pull/57099
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Giovanni Bucci
2025-05-03 07:28:20 -07:00
committed by GitHub
parent abfb7cc5bc
commit 723d7bb542

View File

@@ -27,6 +27,9 @@ const {
ObjectAssign,
ObjectKeys,
StringPrototypeCharCodeAt,
StringPrototypeIndexOf,
StringPrototypeReplaceAll,
StringPrototypeSlice,
decodeURIComponent,
} = primordials;
@@ -637,6 +640,10 @@ Url.prototype.format = function format() {
}
let protocol = this.protocol || '';
if (protocol && StringPrototypeCharCodeAt(protocol, protocol.length - 1) !== 58 /* : */) {
protocol += ':';
}
let pathname = this.pathname || '';
let hash = this.hash || '';
let host = '';
@@ -646,7 +653,7 @@ Url.prototype.format = function format() {
host = auth + this.host;
} else if (this.hostname) {
host = auth + (
this.hostname.includes(':') && !isIpv6Hostname(this.hostname) ?
StringPrototypeIndexOf(this.hostname, ':') !== -1 && !isIpv6Hostname(this.hostname) ?
'[' + this.hostname + ']' :
this.hostname
);
@@ -658,59 +665,55 @@ Url.prototype.format = function format() {
if (this.query !== null && typeof this.query === 'object') {
query = querystring.stringify(this.query);
}
let search = this.search || (query && ('?' + query)) || '';
if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58/* : */)
protocol += ':';
let newPathname = '';
let lastPos = 0;
for (let i = 0; i < pathname.length; ++i) {
switch (pathname.charCodeAt(i)) {
case CHAR_HASH:
if (i - lastPos > 0)
newPathname += pathname.slice(lastPos, i);
newPathname += '%23';
if (StringPrototypeIndexOf(pathname, '#') !== -1 || StringPrototypeIndexOf(pathname, '?') !== -1) {
let newPathname = '';
let lastPos = 0;
const len = pathname.length;
for (let i = 0; i < len; i++) {
const code = StringPrototypeCharCodeAt(pathname, i);
if (code === CHAR_HASH || code === CHAR_QUESTION_MARK) {
if (i > lastPos) {
newPathname += StringPrototypeSlice(pathname, lastPos, i);
}
newPathname += (code === CHAR_HASH ? '%23' : '%3F');
lastPos = i + 1;
break;
case CHAR_QUESTION_MARK:
if (i - lastPos > 0)
newPathname += pathname.slice(lastPos, i);
newPathname += '%3F';
lastPos = i + 1;
break;
}
}
}
if (lastPos > 0) {
if (lastPos !== pathname.length)
pathname = newPathname + pathname.slice(lastPos);
else
pathname = newPathname;
if (lastPos < len) {
newPathname += StringPrototypeSlice(pathname, lastPos);
}
pathname = newPathname;
}
// Only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
// unless they had them to begin with.
if (this.slashes || slashedProtocol.has(protocol)) {
if (this.slashes || host) {
if (pathname && pathname.charCodeAt(0) !== CHAR_FORWARD_SLASH)
if (pathname && StringPrototypeCharCodeAt(pathname, 0) !== CHAR_FORWARD_SLASH)
pathname = '/' + pathname;
host = '//' + host;
} else if (protocol.length >= 4 &&
protocol.charCodeAt(0) === 102/* f */ &&
protocol.charCodeAt(1) === 105/* i */ &&
protocol.charCodeAt(2) === 108/* l */ &&
protocol.charCodeAt(3) === 101/* e */) {
StringPrototypeCharCodeAt(protocol, 0) === 102/* f */ &&
StringPrototypeCharCodeAt(protocol, 1) === 105/* i */ &&
StringPrototypeCharCodeAt(protocol, 2) === 108/* l */ &&
StringPrototypeCharCodeAt(protocol, 3) === 101/* e */) {
host = '//';
}
}
search = search.replaceAll('#', '%23');
// Escape '#' in search.
if (StringPrototypeIndexOf(search, '#') !== -1) {
search = StringPrototypeReplaceAll(search, '#', '%23');
}
if (hash && hash.charCodeAt(0) !== CHAR_HASH)
if (hash && StringPrototypeCharCodeAt(hash, 0) !== CHAR_HASH) {
hash = '#' + hash;
if (search && search.charCodeAt(0) !== CHAR_QUESTION_MARK)
}
if (search && StringPrototypeCharCodeAt(search, 0) !== CHAR_QUESTION_MARK) {
search = '?' + search;
}
return protocol + host + pathname + search + hash;
};