Expand description
XML tree construction and manipulation (§17, §18, §85 Phase 1).
Complete tree construction/manipulation, namespaces, attributes, dictionaries, entity structures, document ownership, copying, linking, and freeing.
§UPSTREAM-PARITY
The libxml2 tree is an observable data structure. The pointer topology (parent, children, last, next, prev, doc, ns, properties, nsDef) is part of the compatibility contract and must be court-tested.
Key invariants (matching upstream):
node->docpoints to the owning document (or NULL if not owned)node->parentpoints to the parent element (or NULL for root)node->childrenpoints to the first childnode->lastpoints to the last childnode->next/node->prevform a doubly-linked list of siblingsnode->propertiespoints to the first attribute (for elements)node->nsDefpoints to the first namespace declaration (for elements)doc->childrenpoints to the root elementdoc->docpoints to itself (self-reference)
§Ownership model
Documents own all their nodes. When a document is freed, all nodes are freed. Nodes can be moved between documents via unlinking and re-adding.
§Phase 1 status
Complete — all tree operations are implemented. Future phases may add more edge-case handling for historical quirks.
§Upstream contract
Mirrors upstream tree.c and buf.c (SRC-LIBXML2-2.15.0, oracle tree
oracle/historical/src/libxml2-2.15.0/). The tree is an observable data
structure: pointer topology (parent, children, last, next, prev, doc, ns,
properties, nsDef) is part of the compatibility contract and must be
court-tested. Parity target: the system libxml2 2.15.3 oracle.
§Conceptual behavior
Complete tree construction/manipulation: namespaces, attributes, dictionaries, entity structures, document ownership, copying, linking and freeing. Nodes are C-layout mirrors (tree.h); copy/link/free semantics follow xmlCopyNode / xmlAddChild / xmlFreeNodeList / xmlFreeDoc.
§Ownership & safety invariants
Documents own all their nodes; freeing the document frees the subtree.
node->parent, node->doc, node->ns, node->next/prev are borrowed pointers —
never freed by the reader. Allocator domain: xmlMalloc, freed with xmlFree
(atlas/OWNERSHIP_ATLAS.md). SAFETY: the Rust mirrors enforce layout exactly
(#[repr(C)]); _xmlElement is 104 bytes upstream and must stay that size
(R-000139: a 56-byte mirror under-allocated every element declaration).
§Historical quirks & epochs
QUIRK-0002 / LORE-0006: namespace nodes have no parent — a long-standing divergence upstream was aware of since the c14n fix commit 044fc6b7 (2002). E-004: entity-content text nodes became TEXT compact at 2.13.0 (commit 8d04f0ee). The 11.1-N structural alignment (R-000164) pinned doc->children DTD placement, CDATA node names, standalone=-2 and the attribute hash (name,prefix,elem) key order.
§Deliberate oddities
Deliberate oddities preserved for parity: an xmlns= declaration with an empty value yields href pointing at an empty string (not NULL), parsed attributes keep atype=0, the DTD node joins doc->children before the first element, and xmlGetLineNo returns long with the upstream -1 walk for non-element nodes (all R-000164).
§Proving courts
OWNERSHIP and TREE-STRUCTURE court families; TREE-001 (27-block structural
fingerprint of 20 corpus docs x 8 option variants, byte-identical), ASan
full-suite runs, and cargo test --lib (counts generated into
atlas/TEST_COUNTS.json by tools/evidence/test_counts.py). Receipts under
courts/receipts/phase-11.
§Tempting simplifications that would break parity
A tempting simplification is a nicer Rust node type instead of the exact
_xmlNode / _xmlElement mirrors — it would break the C ABI layout
(R-000139 class) and every C consumer reading fields at upstream offsets.
Do not auto-maintain parent pointers for namespace nodes (QUIRK-0002); do
not drop the last/next/prev links — TREE-001 fingerprints them.
§Safety
- The unsafe entry points in this module accept raw pointers that must be
valid, correctly typed, and live for the duration of the call:
_xmlDoc,_xmlNode,_xmlAttr,_xmlNs,_xmlDtd,_xmlBuffer, and NUL-terminatedxmlCharstrings. NULL is permitted only where an individual function’s contract explicitly allows it. - Tree links (
parent,children,last,next,prev,properties,nsDef) must form a consistent, live tree; documents own their node subtrees, so callers must not free a node that still belongs to a live document.
Functions§
- add_
child ⚠ - Add a child node to a parent.
- add_
doc_ ⚠entity - Add an entity declaration to the document’s internal subset (upstream
entities.c
xmlAddDocEntity); creates the internal subset when absent. - add_
dtd_ ⚠entity - Add an entity declaration to the document’s external subset (upstream
entities.c
xmlAddDtdEntity); creates the external subset when absent. - add_
sibling ⚠ - Add a sibling node after another.
- add_
sibling_ ⚠before - Add a sibling node before another.
- child_
element_ ⚠count - Count the child ELEMENT nodes of a node (upstream tree.c
xmlChildElementCount). - copy_
doc ⚠ - Copy a document (deep copy by default).
- copy_
node ⚠ - Copy a node (shallow or deep).
- doc_
get_ root_ element - Get the root element of a document.
- doc_
set_ ⚠root_ element - Set the root element of a document.
- dump_
doc ⚠ - Dump a document to a null-terminated string.
- first_
element_ ⚠child - Return the first child ELEMENT of a node, or NULL (upstream tree.c
xmlFirstElementChild). - free_
doc ⚠ - Free a document and all its contents.
- free_
node ⚠ - Free a single node (without freeing children).
- free_
node_ ⚠list - Free a linked list of nodes.
- get_
doc_ ⚠entity - Get a document entity by name.
- get_
dtd_ ⚠entity - Get an entity declaration from the internal or external subset (upstream
entities.c
xmlGetDtdEntity). - get_
int_ subset - Get the internal DTD subset of a document.
- get_
line_ no - Get the line number of a node.
- get_
ns_ ⚠list - Get a list of namespaces in scope for a node.
- get_
ns_ ⚠prop - Get a namespaced attribute value.
- get_
parameter_ ⚠entity - Get a parameter entity by name.
- get_
prop ⚠ - Get an attribute value by name.
- has_
ns_ ⚠prop - Check whether a node has a namespaced property (upstream tree.c
xmlHasNsProp): returns the attribute pointer or NULL. A NULLnameSpacematches the no-namespace case. - has_
prop ⚠ - Check whether a node has a property with the given name (upstream tree.c
xmlHasProp): returns the attribute pointer or NULL. - last_
element_ ⚠child - Return the last child ELEMENT of a node, or NULL (upstream tree.c
xmlLastElementChild). - new_
cdata_ ⚠block - Create a new CDATA section node.
- new_
child ⚠ - Create a new child element.
- new_
comment ⚠ - Create a new comment node.
- new_doc⚠
- Create a new XML document.
- new_dtd⚠
- Create a new DTD node.
- new_
entity ⚠ - Create a new entity.
- new_
node ⚠ - Create a new XML node.
- new_
node_ ⚠eat_ name - Create a new XML element node whose name is BORROWED (not duplicated).
- new_ns⚠
- Create a new namespace declaration.
- new_pi⚠
- Create a new processing instruction node.
- new_
text ⚠ - Create a new text node.
- next_
element_ ⚠sibling - Return the next ELEMENT sibling of a node, or NULL (upstream tree.c
xmlNextElementSibling). - node_
get_ ⚠content - Get the content of a node, recursively concatenating child text.
- previous_
element_ ⚠sibling - Return the previous ELEMENT sibling of a node, or NULL (upstream tree.c
xmlPreviousElementSibling). - remove_
prop ⚠ - Remove a property from a node.
- search_
ns ⚠ - UPSTREAM-PARITY (tree.c xmlSearchNsSafe): search for a namespace bound to
the given PREFIX in scope of
node. A NULLname_spacesearches the default namespace. The walk only ever reads ELEMENT nodes’nsDefchains (the document is NOT an element: itsoldNslist must never be mistaken for declarations), and a declaration with a NULL href does not bind its prefix. - search_
ns_ ⚠by_ href - Search for a namespace by href (URI).
- set_ns⚠
- Set the namespace of a node.
- set_
ns_ ⚠prop - Set a namespaced attribute.
- set_
prop ⚠ - Set an attribute on a node.
- text_
concat ⚠ - Concatenate text to a node’s content (upstream tree.c
xmlTextConcat): appendsnumbytes ofstrto the node’s text content. Returns 0 on success, -1 on error. - text_
merge ⚠ - Merge the text content of two nodes (upstream tree.c
xmlTextMerge): appendsntext’s content totext’s content and freesntext. Returns the first node, or NULL on error. - unlink_
node ⚠ - Unlink a node from its parent/siblings.
- unset_
ns_ ⚠prop - Remove a namespaced property by name (upstream tree.c
xmlUnsetNsProp). - unset_
prop ⚠ - Remove a property by name from a node (upstream tree.c
xmlUnsetProp): returns 0 on success, -1 if the property does not exist or arguments are NULL. - xml_
strlen ⚠ - Get the length of a null-terminated xmlChar string.