Skip to content

Commit 55cb4e1

Browse files
dinwwwhclaude
andauthored
refactor(router): compare tail and prefix segments in place (#199)
<!-- claude-projects-attribution: {"github_login":"dinwwwh"} --> _Requested by **dinwwwh** · [project thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYE6swgSUM9SyULmBVdRjY2t7)_ ## What `matchesSegment` was a one-line function used in two places: `matchesCatchAll`, for the segments after a catch-all param, and the closure that `createPrefixMatcher` returns. Both now do the comparison in place, with a comment that `undefined` stands for a param, which needs a non-empty segment. ## Why Each top-level function costs V8 about 15k instructions to set up at import, and about as much again to compile when it is first called (see #198). This removes one function from every import. It also removes one compile from the first request that reaches a catch-all, or a prefix with a param. ## Measurements Instruction counts from callgrind, `node --single-threaded`, averaged over hash seeds 1 and 2: | | before | after | |---|---|---| | min / min+gzip | 5043 / 2100 B | 5053 / 2098 B | | import | 2.167M | 2.164M | | REST cold+10 (includes a catch-all request) | 6.041M | 6.017M (−0.4%) | | steady, catch-all / tail | 4985 / 5256 | 4979 / 5249 | | steady, prefix matcher with a param / with `%` | 2453 / 4082 | 2422 / 4052 (−1%) | Cold+3 on the REST set reaches no catch-all, so it changes only by the import saving. ## Checks - Tests pass with 100% coverage. - The model fuzzers, including the late-add and prefix ones, find 0 diffs. - Stryker finds 68 survivors, the same as on main. None are on the new comparisons. --- _Generated by [Claude Code](https://claude.ai/code/session_01VVaw7Sutbw6xMTMyDFeNfw)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent cd598d9 commit 55cb4e1

1 file changed

Lines changed: 7 additions & 6 deletions

File tree

‎packages/router/src/index.ts‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ export function createPrefixMatcher(prefix: `/${string}`): (pathname: `/${string
323323
end = pathname.length
324324
}
325325

326-
if (!matchesSegment(expected, decode(pathname.slice(start, end)))) {
326+
// `undefined` stands for a param, which needs a non-empty segment.
327+
const segment = decode(pathname.slice(start, end))
328+
if (expected === undefined ? segment === '' : segment !== expected) {
327329
return false
328330
}
329331

@@ -627,18 +629,17 @@ function matchesCatchAll(
627629
}
628630

629631
for (let i = 0; i < tail.length; i++) {
630-
if (!matchesSegment(tail[i], segments[end + i]!)) {
632+
const expected = tail[i]
633+
const segment = segments[end + i]!
634+
// `undefined` stands for a param, which needs a non-empty segment.
635+
if (expected === undefined ? segment === '' : segment !== expected) {
631636
return false
632637
}
633638
}
634639

635640
return true
636641
}
637642

638-
function matchesSegment(expected: string | undefined, segment: string): boolean {
639-
return expected === undefined ? segment !== '' : segment === expected
640-
}
641-
642643
// `params` is spread only for a param named `__proto__`, not on every pass.
643644
/* oxlint-disable oxc/no-accumulating-spread */
644645
function toParams<T>(route: Route<T>, segments: readonly string[]): Record<string, string> {

0 commit comments

Comments
 (0)