perf: round 16 — shares-lane & contextMap incremental builders, folder/href/disposition/preview alloc cuts

Finishes the route-level half of the O(N²/page) grouped-listing class ROUND15
fixed inside ResourceList, plus a backend CPU/alloc micro-pack. Every change is
benchmark-gated with a hard rollback rule; no PostgreSQL needed for any arm
(benches/ROUND16.md).

Frontend (vitest):
- F1 "My shares" lanes: the `lanes` $derived.by re-bucketed the whole
  accumulated grant list on every page and every grant edit. SharedLanesBuilder
  re-emits only the fresh page (fan-out + first-appearance header), reusing
  untouched lanes' array refs. 25.5x fewer emit calls, 8.8x wall, O(N²/page)→O(N).
- F2 contextMap (trash/recent/favorites/shared-with-me): each rebuilt a fresh
  N-key Map, re-hashing every accumulated id, per page. primeContextPage holds a
  persistent SvelteMap primed per page (the shipped favoriteIds shape).
  25.5x fewer entry calls, 7.2x wall.
- Extracted the shared O(1) append test (isAppendExtension); F1's gate re-covers it.

Backend (counting-allocator):
- M1 folder display constants Arc::from -> intern_display (3 sites): 3 -> 0 allocs/row.
- M2 build_content_disposition (every download + Range seek): 3 -> 1 alloc, 6x, 2.67x wall.
- M3 nc_href (every NC PROPFIND/REPORT href): Vec+join+format -> one pre-sized
  buffer, keeping urlencoding::encode (byte-identical). 38 -> 27 allocs/op.
- M4 NC preview fileId: collect-then-parse -> borrow-slice parse. 4 -> 0 allocs.

Gates: sharedLanes/listContext.bench.test.ts, examples/bench_round16_micro.rs
(GATE PASS all sections). Frontend: vitest 331 pass, svelte-check clean.
Backend: clippy -D warnings clean, 524 lib tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193NjactJVqfU32gxeJDj8m
This commit is contained in:
Claude
2026-07-19 17:30:22 +00:00
parent 8537b5949a
commit 955f4a7b9f
20 changed files with 1388 additions and 123 deletions
@@ -1,5 +1,7 @@
<script lang="ts">
import { errorMessage } from '$lib/utils/errors';
import { SvelteMap } from 'svelte/reactivity';
import { primeContextPage } from '$lib/utils/listContext';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { onMount } from 'svelte';
@@ -43,14 +45,11 @@
// so the sharer shows up in the vignette (rather than the resource's
// intrinsic `created_by`, which is a stranger for grantees).
const items = $derived(fileFolderGrants.map((it) => it.resource as FileItem | FolderItem));
const contextMap = $derived(
new Map<string, ItemContext>(
fileFolderGrants.map((it) => [
it.resource.id,
{ date: it.granted_at, ownerId: it.granted_by ?? null } satisfies ItemContext
])
)
);
// Persistent reactive map, primed per page in `load()` (benches/ROUND16.md §F2)
// instead of rebuilding a fresh Map that re-hashes the whole accumulated list
// on every infinite-scroll page. Drives are skipped (they never reach the
// row UI), so the map covers exactly the displayed `fileFolderGrants`.
const contextMap = new SvelteMap<string, ItemContext>();
// Server-supported sort_by values (see grant_handler.rs:615):
// granted_at, granted_by, name, type
@@ -96,6 +95,11 @@
reverse: rev
});
raw = reset ? page.items : [...raw, ...page.items];
primeContextPage(contextMap, reset, page.items, (it) =>
it.resource_type === 'drive'
? null
: [it.resource.id, { date: it.granted_at, ownerId: it.granted_by ?? null }]
);
cursor = page.next_cursor;
// Warm the sharer-name cache so the "Shared by" group headers
// show real names instead of UUIDs.