Skip to content

Commit 2497cd3

Browse files
gurgundaymarco-ippolito
authored andcommitted
url: optimize URLSearchParams set/delete duplicate handling
PR-URL: #62266 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent 1dadbee commit 2497cd3

2 files changed

Lines changed: 78 additions & 14 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
4+
const bench = common.createBenchmark(main, {
5+
method: ['set', 'delete'],
6+
type: ['unique', 'duplicates'],
7+
count: [10, 1000],
8+
n: [1e4],
9+
});
10+
11+
function buildSeed(type, count) {
12+
const parts = new Array(count);
13+
14+
if (type === 'duplicates') {
15+
for (let i = 0; i < count; i++) {
16+
parts[i] = `dup=${i}`;
17+
}
18+
} else {
19+
for (let i = 0; i < count; i++) {
20+
parts[i] = `k${i}=${i}`;
21+
}
22+
}
23+
24+
return new URLSearchParams(parts.join('&'));
25+
}
26+
27+
function main({ method, type, count, n }) {
28+
const seed = buildSeed(type, count);
29+
const key = type === 'duplicates' ? 'dup' : 'k0';
30+
const mutate = method === 'set' ?
31+
(params) => params.set(key, 'updated') :
32+
(params) => params.delete(key);
33+
34+
for (let i = 0; i < 1e3; i++) {
35+
mutate(new URLSearchParams(seed));
36+
}
37+
38+
bench.start();
39+
for (let i = 0; i < n; i++) {
40+
mutate(new URLSearchParams(seed));
41+
}
42+
bench.end(n);
43+
}

lib/internal/url.js

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -494,26 +494,37 @@ class URLSearchParams {
494494

495495
const list = this.#searchParams;
496496
name = StringPrototypeToWellFormed(`${name}`);
497+
const { length } = list;
498+
let write = 0;
497499

498500
if (value !== undefined) {
499501
value = StringPrototypeToWellFormed(`${value}`);
500-
for (let i = 0; i < list.length;) {
502+
for (let i = 0; i < length; i += 2) {
501503
if (list[i] === name && list[i + 1] === value) {
502-
list.splice(i, 2);
503-
} else {
504-
i += 2;
504+
continue;
505+
}
506+
if (write !== i) {
507+
list[write] = list[i];
508+
list[write + 1] = list[i + 1];
505509
}
510+
write += 2;
506511
}
507512
} else {
508-
for (let i = 0; i < list.length;) {
513+
for (let i = 0; i < length; i += 2) {
509514
if (list[i] === name) {
510-
list.splice(i, 2);
511-
} else {
512-
i += 2;
515+
continue;
516+
}
517+
if (write !== i) {
518+
list[write] = list[i];
519+
list[write + 1] = list[i + 1];
513520
}
521+
write += 2;
514522
}
515523
}
516524

525+
if (write !== length)
526+
list.length = write;
527+
517528
if (this.#context) {
518529
setURLSearchParamsModified(this.#context);
519530
}
@@ -593,24 +604,34 @@ class URLSearchParams {
593604
const list = this.#searchParams;
594605
name = StringPrototypeToWellFormed(`${name}`);
595606
value = StringPrototypeToWellFormed(`${value}`);
607+
const { length } = list;
596608

597609
// If there are any name-value pairs whose name is `name`, in `list`, set
598610
// the value of the first such name-value pair to `value` and remove the
599611
// others.
600612
let found = false;
601-
for (let i = 0; i < list.length;) {
613+
let write = 0;
614+
for (let i = 0; i < length; i += 2) {
602615
const cur = list[i];
616+
let keep = true;
603617
if (cur === name) {
604618
if (!found) {
605-
list[i + 1] = value;
619+
list[write] = cur;
620+
list[write + 1] = value;
606621
found = true;
607-
i += 2;
608622
} else {
609-
list.splice(i, 2);
623+
keep = false;
610624
}
611-
} else {
612-
i += 2;
625+
} else if (write !== i) {
626+
list[write] = cur;
627+
list[write + 1] = list[i + 1];
613628
}
629+
if (keep)
630+
write += 2;
631+
}
632+
633+
if (found && write !== length) {
634+
list.length = write;
614635
}
615636

616637
// Otherwise, append a new name-value pair whose name is `name` and value

0 commit comments

Comments
 (0)