Skip to content

Add FlatNode: read-only columnar full-DOM reader (#82)#84

Merged
mathieu17g merged 3 commits into
v0.5-devfrom
flatnode
Jul 5, 2026
Merged

Add FlatNode: read-only columnar full-DOM reader (#82)#84
mathieu17g merged 3 commits into
v0.5-devfrom
flatnode

Conversation

@mathieu17g

Copy link
Copy Markdown
Collaborator

Implements #82FlatNode, the fourth reader: parse(xml, FlatNode) materializes the whole document once into a contiguous store of isbits records (index links + zero-copy byte ranges into the retained source; text/attribute values entity-decoded at access). Node's read half at Cursor's GC cost.

Measured in-package on the 13.5 MiB XMark corpus (882k nodes): build 51.6 ms / 35.5 MiB alloc vs Node 117.3 ms / 122.3 MiB — 2.3× faster, 3.4× fewer allocations, matching the validated prototype; GC pressure collapses (the collector traces a handful of arrays instead of ~2.5 objects per node).

Guarantees, all test-enforced:

  • W3C parity with the Node parser: decoded-equivalent trees on all 776 well-formed documents; identical accept/reject verdicts on all 1257 not-well-formed ones. The parity harness caught (and this PR fixes) one real divergence on first run: empty <!---->/<![CDATA[]]> must be "", not nothing (tri-state record: absent / empty / present).
  • Same wellformed levels and identical error messages as parse(_, Node).
  • ==/hash on FlatNode are positional identity (same store, same node) — the Node identity: === and hash redesign (planned for v0.5) #83 semantics, debuting on the new reader; unique/Dict behave.
  • Extras from the stored links: O(1) parent(node), O(depth) 1-arg depth(node); Node(flatnode) materializes a subtree as a mutable Node; XML.write accepts FlatNode.
  • By design: read-only, whole-store retention, 2 GiB/typemax(Int32) guard (clear error pointing at Node).

Also adds the reader-orientation table to the README (the four readers behind one accessor set). Targets v0.5-dev.

The fourth reader alongside Node/LazyNode/Cursor: parse(xml, FlatNode)
materializes the whole document once into a contiguous store of isbits
records (parent/first_child/next_sibling index links, zero-copy byte
ranges into the retained source), so building is fast, random access is
O(1), and the GC traces a handful of arrays instead of one object per
node. Text and attribute values are entity-decoded at access (the
has_entities token flag rides the sign bit of the stored length).

The builder is the same single visibly-pushdown pass as _parse with the
same well-formedness checks at the same token points (shared check
functions; identical error messages, asserted by the parity tests).
FlatNode == / hash are positional identity — same store, same node
(the #83 semantics, debuting on the new reader). Node(flatnode)
materializes a subtree as a mutable Node; XML.write accepts FlatNode.
O(1) parent and O(depth) 1-arg depth come free from the stored links.

Assisted-by: Claude (Anthropic)
The new W3C parity testset asserts FlatNode ≡ Node (decoded trees) on
every well-formed document of the pinned conformance suite, and the
exact same accept/reject verdict on every not-well-formed one — any
divergence between the flat builder and _parse is a named failure.

It caught exactly one on first run, on 5 documents (sun/invalid/empty.xml,
oasis p15pass1/p18pass1, ibm P15/P20): an empty comment <!----> or CDATA
<![CDATA[]]> has value "" on Node but came back nothing on FlatNode,
because the record encoded absence as len == 0. Absence is now
value_offset == -1 (empty content keeps a real offset with len 0),
matching the Node parser on all three states: absent, empty, present.

Assisted-by: Claude (Anthropic)
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 96.56652% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.76%. Comparing base (a3f57d8) to head (85ec3e4).

Files with missing lines Patch % Lines
src/flatnode.jl 96.56% 8 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##           v0.5-dev      #84      +/-   ##
============================================
+ Coverage     94.47%   94.76%   +0.28%     
============================================
  Files            11       12       +1     
  Lines          1793     2026     +233     
============================================
+ Hits           1694     1920     +226     
- Misses           99      106       +7     
Files with missing lines Coverage Δ
src/XML.jl 100.00% <ø> (ø)
src/flatnode.jl 96.56% <96.56%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mathieu17g mathieu17g merged commit 3700c83 into v0.5-dev Jul 5, 2026
8 checks passed
@mathieu17g mathieu17g deleted the flatnode branch July 5, 2026 17:17
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.

2 participants