Skip to content

Commit 937c02d

Browse files
authored
Minimize array allocations (#437)
1 parent 57247e6 commit 937c02d

File tree

3 files changed

+45
-41
lines changed

3 files changed

+45
-41
lines changed

src/index.bench.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import { describe, bench } from "vitest";
2-
import { compile, match, parse } from "./index.js";
2+
import { compile, match, parse, pathToRegexp } from "./index.js";
33

4-
describe("parse", () => {
5-
const PATHS: string[] = [
6-
"/api",
7-
"/user/:id",
8-
"/files/*filepath",
9-
"/:foo-:bar",
10-
'/quoted-:"param"',
11-
"/complex/:param1-:param2/*rest",
12-
];
4+
const PATHS: string[] = [
5+
"/api",
6+
"/user/:id",
7+
"/user/:id{/:extra}",
8+
"/files/*path",
9+
"/:param1-:param2",
10+
'/quoted-:"param1"',
11+
"/complex/:param1-:param2/*path",
12+
"/name{/:attr1}{-:attr2}{-:attr3}",
13+
];
1314

15+
describe("parse", () => {
1416
bench("parsing paths", () => {
1517
for (const path of PATHS) parse(path);
1618
});
1719
});
1820

21+
describe("toRegexp", () => {
22+
bench("building regexp", () => {
23+
for (const path of PATHS) pathToRegexp(path);
24+
});
25+
});
26+
1927
describe("match", () => {
2028
const PATHS: string[] = [
2129
"/xyz",
@@ -59,15 +67,7 @@ describe("match", () => {
5967
});
6068

6169
describe("compile", () => {
62-
const PATH_FNS = [
63-
"/api",
64-
"/user/:id",
65-
"/user/:id{/:extra}",
66-
"/files/*path",
67-
"/:param1-:param2",
68-
'/quoted-:"param1"',
69-
"/complex/:param1-:param2/*path",
70-
].map((path) => compile(path));
70+
const PATH_FNS = PATHS.map((path) => compile(path));
7171

7272
bench("compiling paths", () => {
7373
for (const fn of PATH_FNS) {

src/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ describe("path-to-regexp", () => {
200200
} catch (error) {
201201
const stack = (error as Error).stack
202202
?.split("\n")
203-
.slice(0, 5)
203+
.slice(0, 6)
204204
.join("\n");
205205
expect(stack).toContain("index.spec.ts");
206206
}

src/index.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -351,17 +351,18 @@ function tokenToFunction(
351351
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
352352
}
353353

354-
return value
355-
.map((value, index) => {
356-
if (typeof value !== "string") {
357-
throw new TypeError(
358-
`Expected "${token.name}/${index}" to be a string`,
359-
);
360-
}
354+
let result = "";
361355

362-
return encodeValue(value);
363-
})
364-
.join(delimiter);
356+
for (let i = 0; i < value.length; i++) {
357+
if (typeof value[i] !== "string") {
358+
throw new TypeError(`Expected "${token.name}/${i}" to be a string`);
359+
}
360+
361+
if (i > 0) result += delimiter;
362+
result += encodeValue(value[i]);
363+
}
364+
365+
return result;
365366
};
366367
}
367368

@@ -453,29 +454,30 @@ export function pathToRegexp(
453454
trailing = true,
454455
} = options;
455456
const keys: Keys = [];
456-
const sources: string[] = [];
457-
const paths: Array<Path | Path[]> = [path];
457+
let source = "";
458458
let combinations = 0;
459459

460-
while (paths.length) {
461-
const path = paths.shift()!;
462-
460+
function process(path: Path | Path[]) {
463461
if (Array.isArray(path)) {
464-
paths.push(...path);
465-
continue;
462+
for (const p of path) process(p);
463+
return;
466464
}
467465

468466
const data = typeof path === "object" ? path : parse(path, options);
469467
flatten(data.tokens, 0, [], (tokens) => {
470-
if (combinations++ >= 256) {
468+
if (combinations >= 256) {
471469
throw new PathError("Too many path combinations", data.originalPath);
472470
}
473471

474-
sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath));
472+
if (combinations > 0) source += "|";
473+
source += toRegExpSource(tokens, delimiter, keys, data.originalPath);
474+
combinations++;
475475
});
476476
}
477477

478-
let pattern = `^(?:${sources.join("|")})`;
478+
process(path);
479+
480+
let pattern = `^(?:${source})`;
479481
if (trailing) pattern += "(?:" + escape(delimiter) + "$)?";
480482
pattern += end ? "$" : "(?=" + escape(delimiter) + "|$)";
481483

@@ -495,9 +497,11 @@ function flatten(
495497
const token = tokens[index++];
496498

497499
if (token.type === "group") {
498-
flatten(token.tokens, 0, result.slice(), (seq) =>
500+
const len = result.length;
501+
flatten(token.tokens, 0, result, (seq) =>
499502
flatten(tokens, index, seq, callback),
500503
);
504+
result.length = len;
501505
continue;
502506
}
503507

0 commit comments

Comments
 (0)