Expand description
Memory management (§58, §85 Phase 1).
Wraps the allocator hooks from crate::abi::allocator for use by the XML
implementation modules. This module provides the internal Rust interface
to the allocator system.
§UPSTREAM-PARITY
libxml2 exposes several memory management APIs:
xmlMemSetup/xmlMemGet— set/get custom allocator hooksxmlGcMemSetup/xmlGcMemGet— GC-aware variants (now identical)xmlMalloc/xmlMallocAtomic/xmlRealloc/xmlFree/xmlMemStrdupxmlMallocZero/xmlMallocAtomicZero/xmlReallocZeroxmlMemUsed/xmlMemBlocks— debugging statisticsxmlMemDisplay/xmlMemShow— debugging outputxmlInitMemory/xmlCleanupMemory— lifecycle
All of these are implemented in crate::abi::allocator. This module
re-exports them for internal use.
§Phase 1 status
Complete — all memory functions delegate to the ABI allocator layer.
§Upstream contract
Mirrors upstream xmlmemory.c (SRC-LIBXML2-2.15.0-XMLMEMORY-C): xmlMemSetup
/ xmlMemGet / xmlGcMemSetup / xmlMemUsed / xmlMemBlocks / xmlMemDisplay /
xmlMemShow and the xmlMalloc* family. The actual implementation lives in
crate::abi::allocator; this module is the internal Rust interface.
§Conceptual behavior
There are two allocation planes, exactly as in upstream 2.15.0. The five
exported variables (xmlMalloc, xmlMallocAtomic, xmlRealloc,
xmlFree, xmlMemStrdup) are the hook system: their default bodies are
plain libc malloc/realloc/free/strdup wrappers and are UNTRACKED —
with the default installed xmlMemUsed()/xmlMemBlocks()/xmlMemSize()
all return 0, byte-identical with the oracle (R-000178). xmlMemSetup /
direct variable assignment re-route the hooks, and custom allocators
bypass accounting entirely, matching upstream’s debug-allocator-only block
table. The debug-named surface (xmlMemMalloc/xmlMemFree/xmlMemRealloc/
xmlMemoryStrdup and the *Loc variants) is the second plane: always
libc-backed and tracked by the per-block registry (R-000131), which is
what xmlMemSize returns sizes from for those blocks. The display entry
points (xmlMemDisplay, xmlMemDisplayLast, xmlMemShow,
xmlMemoryDump) are no-ops matching upstream 2.15.0, which removed that
feature.
§Ownership & safety invariants
Ownership rule (atlas/OWNERSHIP_ATLAS.md): a pointer returned by an xml*
allocator must be freed with xmlFree; a pointer from libc::calloc inside
the engine is freed internally and never escapes. SAFETY: xmlFree on a
foreign/unknown pointer is a plain libc free — the default free body
does not consult the registry at all (the registry is only consulted by
the debug-named xmlMemFree), exactly like upstream’s default
xmlFree = free (R-000178).
§Historical quirks & epochs
R-000178 (11.1-Z.3): the pre-Z.3 default allocator routed through Rust’s
global allocator with fabricated Layouts — invalid-layout UB under the
Rust allocator contract; replaced with plain libc
malloc/realloc/free/strdup (C allocation semantics; no layout
exists). The pre-Z.3 claim that xmlFree on a foreign pointer was a
“no-op removal from the registry” is obsolete: the default free is now
untracked libc free. R-000131 (11.1-J): xmlMemSize returns the
recorded size for debug-surface blocks and the *Loc variants
accept-and-ignore file/line exactly like upstream 2.15.0’s
ATTRIBUTE_UNUSED parameters. R-000133 (11.1-H): the legacy debug names
were declared-but-unexported and had to be implemented for the
honest-header rule.
§Deliberate oddities
Deliberate oddities: the exported allocator entry points are DATA
function-pointer globals matching the oracle ABI (R-000162: upstream
exports them as data variables so the xmlMalloc = custom override can
link), and since 11.1-Z.2 they are the single source of truth —
xmlMemSetup assigns them and every internal allocation reads them
through the *Impl indirection, so xmlMemSetup and direct
xmlMalloc = custom assignment share one override mechanism (R-000176).
The debug-named functions deliberately do NOT route through the variables
(upstream’s debug allocator is independent of the hooks).
§Proving courts
ABI-DATA, ALLOCATOR, GLOBAL-STATE and THREADING court families;
ALLOCATOR-DEFAULT-001 (default-allocator contract: many sizes, zero-size,
grow/shrink realloc, realloc-to-zero, failure, strdup, direct
exported-variable calls, long churn, xmlMemSize/xmlMemUsed/
xmlMemBlocks exactness — byte-identical with the oracle, R-000178);
ALLOCATOR-HOOK (custom-hook differential, byte-identical); DATA-GLOBALS-001
(allocator globals byte-identical); DSO-LOADER (every exported symbol
resolved from the built DSO); and cargo test --lib (counts generated
into atlas/TEST_COUNTS.json by tools/evidence/test_counts.py).
§Tempting simplifications that would break parity
A tempting simplification is routing all allocation through the Rust
global allocator — the pre-Z.3 std::alloc fabricated-Layout approach was
invalid-layout UB (R-000178), and any Rust-allocator route would break
xmlMemSetup overrides and the exported xmlMalloc data-symbol ABI
(R-000162). Do not make the default tracked: returning nonzero
xmlMemUsed/xmlMemBlocks under the default diverges from the oracle’s
0s. Do not restore the display dumps: upstream 2.15.0 removed them, so a
per-block dump would diverge.
Re-exports§
pub use crate::abi::allocator::xmlFreeImpl;pub use crate::abi::allocator::xmlInitMemory;pub use crate::abi::allocator::xmlMallocAtomicImpl;pub use crate::abi::allocator::xmlMallocAtomicZero;pub use crate::abi::allocator::xmlMallocImpl;pub use crate::abi::allocator::xmlMallocZero;pub use crate::abi::allocator::xmlMemBlocks;pub use crate::abi::allocator::xmlMemDisplay;pub use crate::abi::allocator::xmlMemGet;pub use crate::abi::allocator::xmlMemSetup;pub use crate::abi::allocator::xmlMemShow;pub use crate::abi::allocator::xmlMemStrdupImpl;pub use crate::abi::allocator::xmlMemUsed;pub use crate::abi::allocator::xmlReallocImpl;pub use crate::abi::allocator::xmlReallocZero;
Functions§
- cleanup_
memory - Clean up the memory subsystem.
- init_
memory - Initialize the memory subsystem.