Repository: nodejs/node
Pull request: url: improve performance of the format function

As part of my ongoing review of the `assert` codebase, I started examining the `lib/url.js` file and optimizing some straightforward code that could be further refined for better performance.

Here are the benchmark results for the `benchmarks/url/url-format.js` file:


```bash

OLD:

  url/url-format.js n=25000000 type="slashes": 23,427,608.256140668
url/url-format.js n=25000000 type="file": 15,799,023.358977374

NEW: 

 url/url-format.js n=25000000 type="slashes": 52,260,146.83010853
url/url-format.js n=25000000 type="file": 23,652,608.462879658
```

For type="slashes":
123% improvement


For type="file":
50% improvement

Changed files:
filename: lib/url.js
patch: @@ -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';
-        lastPos = i + 1;
-        break;
-      case CHAR_QUESTION_MARK:
-        if (i - lastPos > 0)
-          newPathname += pathname.slice(lastPos, i);
-        newPathname += '%3F';
+  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;
+      }
     }
-  }
-  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;
 };