Skip to content

release: 0.3.15#207

Merged
dginev merged 3 commits into
mainfrom
release-0.3.15
Jul 7, 2026
Merged

release: 0.3.15#207
dginev merged 3 commits into
mainfrom
release-0.3.15

Conversation

@dginev

@dginev dginev commented Jul 7, 2026

Copy link
Copy Markdown
Member

Cuts 0.3.15: pins the [Unreleased] CHANGELOG section to 0.3.15 (2026-07-06),
opens a fresh empty [Unreleased], and bumps Cargo.toml 0.3.14 → 0.3.15.

Everything in the release already merged to main via #205 and #206; this is the
version/CHANGELOG bump only (no code changes). Contents:

Added

  • reader::TextReader — safe xmlTextReader pull parser for very large documents;
    expand() (borrowed RoNode) / expand_to_document() (owned, namespace-reconciled
    Document) materialize one subtree at a time.
  • xpath checked evaluators (evaluate_checked / node_evaluate_checked /
    node_evaluate_readonly_checked) surfacing libxml2's structured XPathError,
    incl. the >10M-nodeset "growing nodeset hit limit".

Changed

  • FxHash-style hasher for the xmlNodePtr -> Node cache (~28–30% wall on node/math-heavy
    docs); dependency-free (std::hash only), output-identical.

Fixed

  • get_properties/_ns read values directly from the attr node (xmlNodeGetContent)
    instead of a quadratic by-name xmlGetProp re-scan.
  • Two catch_unwind-bypassing SIGSEGV hardenings: NULL-guard the c_helpers
    raw-pointer field accessors (segfault at 8) and the CStr::from_ptr surface against
    strlen(NULL) (segfault at 0).

After merge: tag v0.3.15 on the merge commit, then cargo publish.

🤖 Generated with Claude Code

dginev and others added 3 commits July 6, 2026 20:37
Pin the [Unreleased] section to 0.3.15 (2026-07-06) and bump the crate
version 0.3.14 -> 0.3.15. Opens a fresh empty [Unreleased] section.

Landed since 0.3.14 (via #205 and #206):

Added
* reader::TextReader — safe xmlTextReader pull parser for very large
  documents; expand() (borrowed RoNode) / expand_to_document() (owned,
  namespace-reconciled Document) materialize one subtree at a time.
* xpath checked evaluators (evaluate_checked / node_evaluate_checked /
  node_evaluate_readonly_checked) surfacing libxml2's structured
  XPathError, incl. the >10M-nodeset "growing nodeset hit limit".

Changed
* FxHash-style hasher for the xmlNodePtr->Node cache (~28-30% wall on
  node/math-heavy docs); dependency-free (std::hash only), output-identical.

Fixed
* get_properties/_ns read values directly from the attr node
  (xmlNodeGetContent) instead of a quadratic by-name xmlGetProp re-scan.
* Two SIGSEGV hardenings that bypassed catch_unwind: NULL-guard the
  c_helpers raw-pointer field accessors (segfault at 8) and the
  CStr::from_ptr surface against strlen(NULL) (segfault at 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…us type

`xmlTextReaderNodeType` yields an `xmlReaderTypes` value that coincides with
`xmlElementType` only for 1..=12. The reader-only events collide with unrelated
element types — END_ELEMENT (15) == XML_ELEMENT_DECL, WHITESPACE (13) ==
XML_HTML_DOCUMENT_NODE, SIGNIFICANT_WHITESPACE (14) == XML_DTD_NODE — so passing
them straight through `NodeType::from_int` mislabeled every closing `</x>` as an
`ElementDecl`, contradicting the method's own doc contract ("end-of-element →
None, distinguishing an opening `<x>` from a closing `</x>`"). Only translate the
coinciding range; reader-only events have no `NodeType` equivalent → None.
`is_element()` is unaffected (None and ElementDecl both differ from ElementNode).

Found while adding TextReader error/edge tests (also here): from_file on a
missing path → Err, malformed XML surfaces Err from read (not a silent Ok(false)),
read_to_next with no match reaches EOF → Ok(false), and node_type open-vs-close.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The *_checked evaluators' Ok path was exercised transitively (the thin
evaluate/node_evaluate wrappers delegate to them), but the Err/classification
path — the whole reason the API exists for very large documents — had no tests.
Add: an invalid expression yields a populated XPathError (message + non-zero
code) while the thin wrapper still errors; is_nodeset_limit recognizes exactly
libxml2's growing-nodeset/OOM messages (constructed directly, since a real >10M
node-set is impractical to materialize); and the node-relative checked
evaluators (both &Node and RoNode) resolve against the context node and surface
errors identically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dginev dginev merged commit e3323da into main Jul 7, 2026
18 checks passed
@dginev dginev deleted the release-0.3.15 branch July 7, 2026 10:53
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