Skip to content

Commit 07a4b40

Browse files
committed
url: optimize URLSearchParams set/delete duplicate handling
1 parent 8ccbe8e commit 07a4b40

File tree

2 files changed

+78
-14
lines changed

2 files changed

+78
-14
lines changed
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
@@ -495,26 +495,37 @@ class URLSearchParams {
495495

496496
const list = this.#searchParams;
497497
name = StringPrototypeToWellFormed(`${name}`);
498+
const len = list.length;
499+
let write = 0;
498500

499501
if (value !== undefined) {
500502
value = StringPrototypeToWellFormed(`${value}`);
501-
for (let i = 0; i < list.length;) {
503+
for (let i = 0; i < len; i += 2) {
502504
if (list[i] === name && list[i + 1] === value) {
503-
list.splice(i, 2);
504-
} else {
505-
i += 2;
505+
continue;
506+
}
507+
if (write !== i) {
508+
list[write] = list[i];
509+
list[write + 1] = list[i + 1];
506510
}
511+
write += 2;
507512
}
508513
} else {
509-
for (let i = 0; i < list.length;) {
514+
for (let i = 0; i < len; i += 2) {
510515
if (list[i] === name) {
511-
list.splice(i, 2);
512-
} else {
513-
i += 2;
516+
continue;
517+
}
518+
if (write !== i) {
519+
list[write] = list[i];
520+
list[write + 1] = list[i + 1];
514521
}
522+
write += 2;
515523
}
516524
}
517525

526+
if (write !== len)
527+
list.length = write;
528+
518529
if (this.#context) {
519530
setURLSearchParamsModified(this.#context);
520531
}
@@ -594,24 +605,34 @@ class URLSearchParams {
594605
const list = this.#searchParams;
595606
name = StringPrototypeToWellFormed(`${name}`);
596607
value = StringPrototypeToWellFormed(`${value}`);
608+
const len = list.length;
597609

598610
// If there are any name-value pairs whose name is `name`, in `list`, set
599611
// the value of the first such name-value pair to `value` and remove the
600612
// others.
601613
let found = false;
602-
for (let i = 0; i < list.length;) {
614+
let write = 0;
615+
for (let i = 0; i < len; i += 2) {
603616
const cur = list[i];
617+
let keep = true;
604618
if (cur === name) {
605619
if (!found) {
606-
list[i + 1] = value;
620+
list[write] = cur;
621+
list[write + 1] = value;
607622
found = true;
608-
i += 2;
609623
} else {
610-
list.splice(i, 2);
624+
keep = false;
611625
}
612-
} else {
613-
i += 2;
626+
} else if (write !== i) {
627+
list[write] = cur;
628+
list[write + 1] = list[i + 1];
614629
}
630+
if (keep)
631+
write += 2;
632+
}
633+
634+
if (found && write !== len) {
635+
list.length = write;
615636
}
616637

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

0 commit comments

Comments
 (0)