Skip to content

feat: v0.3 query ownership and complexity fixes - #4

Closed
OrlovEvgeny wants to merge 1 commit into
masterfrom
feat/v0.3-query-ownership-and-complexity
Closed

OrlovEvgeny wants to merge 1 commit into
masterfrom
feat/v0.3-query-ownership-and-complexity

Conversation

@OrlovEvgeny

Copy link
Copy Markdown
Owner

Introduce a Query scope that owns everything a query produces, and remove the quadratic paths and recursion limits the new benchmark harness exposed.

Ownership:

  • Add Query (src/query.zig) with its own scratch arena. Query results, chained Selection states, parsed selectors and the strings returned by html(), text() and outerHtml() now live and die with the query instead of the document, so repeated queries against a long-lived document no longer grow it. reset() reclaims a round while keeping capacity; DOM mutations stay owned by the document.

Complexity:

  • wouldCreateCycle ran under an assert evaluated in every build mode, making tree building O(nodes x depth).
  • autoClose scanned the whole open-element stack per start tag.
  • :has() re-scanned the entire document per anchor.
  • :nth-child() recounted a sibling list per candidate.
  • Render, clone, validate, text extraction and matching now traverse iteratively, so deeply nested documents no longer overflow the stack.

Benchmarks:

  • Add zig build bench (always ReleaseFast) with deterministic corpora, per-operation memory accounting and an empirical complexity check (--scaling), plus v0.2 and current baselines.
  • CI gates the two things that are not noisy on shared runners: no benchmark may retain memory in the document arena, and no workload may scale superlinearly. zig fmt --check now covers bench/.

Introduce a Query scope that owns everything a query produces, and remove
the quadratic paths and recursion limits the new benchmark harness exposed.

Ownership:

- Add `Query` (src/query.zig) with its own scratch arena. Query results,
  chained `Selection` states, parsed selectors and the strings returned by
  `html()`, `text()` and `outerHtml()` now live and die with the query
  instead of the document, so repeated queries against a long-lived
  document no longer grow it. `reset()` reclaims a round while keeping
  capacity; DOM mutations stay owned by the document.

Complexity:

- `wouldCreateCycle` ran under an `assert` evaluated in every build mode,
  making tree building O(nodes x depth).
- `autoClose` scanned the whole open-element stack per start tag.
- `:has()` re-scanned the entire document per anchor.
- `:nth-child()` recounted a sibling list per candidate.
- Render, clone, validate, text extraction and matching now traverse
  iteratively, so deeply nested documents no longer overflow the stack.

Benchmarks:

- Add `zig build bench` (always ReleaseFast) with deterministic corpora,
  per-operation memory accounting and an empirical complexity check
  (`--scaling`), plus v0.2 and current baselines.
- CI gates the two things that are not noisy on shared runners: no
  benchmark may retain memory in the document arena, and no workload may
  scale superlinearly. `zig fmt --check` now covers bench/.

Also: drop the leftover `src/main.zig` and debug test harnesses, bump the
version to 0.3.0, and document the Document/Query split in the README.

Claude-Session: https://claude.ai/code/session_01Be1r7Z8EYKSsJzePV8gfsB
@OrlovEvgeny OrlovEvgeny closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant