libxml-rs
Phase 16: Performance parity — SIMD structural scanning, differential courts, and the five-consumer drop-in.
Custodial native-Rust reimplementation of the libxml2 + libxslt ecosystem: a forensic reconstruction of observable behavior, implemented in native Rust, with C ABI compatibility for drop-in replacement.
This is not an XML crate. This is not an XSLT crate. This is not a wrapper. This is a custodial forensic archive and native-Rust reimplementation of the complete observable behavior of libxml2 and libxslt across their historical lifetimes.
Current Status: Phase 16 — performance parity
Phase 16 treats performance as a forensic parity surface: every claim is a
measured, statistically classified row against a frozen canonical oracle
(libxml2 2.15.3 + libxslt 1.1.45, built into the libxml-rs/phase14-debian
court image), with the convention speedup = oracle_time / candidate_time.
Evidence lives in atlas/PERFORMANCE_ATLAS.md / .json and
courts/receipts/phase-16/ (baseline, profiling, per-change receipts, raw
matrices). Work so far, in phase order:
- §16.0/16.4 — baseline + profiling:
PHASE-16-BASELINE.md/.json(host/build fingerprints, frozen-oracle identity),PHASE-16-4-PROFILING.md. - §16.5/16.6 — scalar engine: XML 2.11/3.3.3 byte-model fidelity;
owned-or-span text tokens (character runs reference the base input instead
of copying); decode-once scanner loops; contiguous printable-ASCII text
scanning; duplicate-attribute detection without per-name clones;
allocator bridge caches the hook-slot address; borrowed synchronous
memory input; bulk ASCII name + whitespace scanning. Also removed a
default-handler O(N²) text-merge and a bare-
&infinite loop found along the way. - §16.7 — SIMD structural scanning (
src/xml/parser/scan/): the printable-ASCII text-run classifier is runtime-dispatched across three byte-identical backends — scalar reference, AVX2 (32-byte lanes), and AVX-512BW (64-byte k-masks) — selected once per process (LIBXML_RS_SCAN_BACKEND=scalar|avx2|avx512|autodiagnostic override). The §16.7.7 differential court parses 1191 adversarial + fixture documents under every backend and the oracle: backend-invariance (stdout+stderr) is byte-identical and candidate parse trees match the oracle byte-for-byte. - Bugs the court caught and fixed: a release-only crash (the streamed
generic-error asm trampolines declared caller-saved argument registers as
plain inputs — LLVM reused a stale register, so every
XML_ERR_INVALID_ENCODINGraise freed a stack pointer) and seven error-path parity gaps (incomplete UTF-8 at EOF, theBytes:dump length, the]]>caret at the run start, NUL/control characters in text and attribute values,+no longer accepted in XML names, self-closing/requiring>, end tags requiring>). - §16.7.5 — AVX-512 policy evidence: an alternating-order, taskset-pinned
matrix shows no end-to-end AVX-512 advantage over AVX2 on this Zen 5 host
for element-heavy documents;
autotherefore stays on AVX2 (no manufactured width win). Raw rows:courts/receipts/phase-16/raw/scan-backend-matrix.csv. - Known blocker (documented):
xmlParseChunkre-parses the whole accumulated input per non-final chunk (helpers.rs parse_chunk, the SP-14.3.1-6 PHP expat-compat design), which is O(N²) on long chunked feeds (oracle: linear 1.6 GB/s; candidate 96 s vs 0.05 s at 80 MB). It blocks the lxmliterparselarge-document test and the §16.12.3 lxml iterparse consumer benchmark — the next big mover (courts/receipts/phase-16/16-7-7-push-reparse.md). - Performance infrastructure: Criterion benches (
benches/), provider-isolated oracle/candidate harness + Pareto matrix (tools/bench/), per-backend fuzz targets (fuzz/), and the five-consumer court surface (xmllint, xsltproc, python3-lxml, ruby-nokogiri, PHP) with the corpus/court machinery incourts/suites/phase16/.
Latest gates: cargo test --lib 1282 passed / 0 failed; PHP six-extension
six-gate 1250 passed / 0 failed; CLI xmllint differential 46/48 (2
pre-existing); per-backend parse fuzz clean.
Published artifacts
- crates.io:
libxml-rs0.1.0-alpha.48 - GitHub: https://github.com/infinityabundance/libxml-rs
Oracle verification
The frozen canonical oracle — libxml2 2.15.3 and libxslt 1.1.45 — is built
from source into the libxml-rs/phase14-debian court image (12 further
historical libxml2 releases live under oracle/historical/ for the Phase-11
epoch matrix). The candidate (libxml-rs) does not link against system
libxml2/libxslt — verified by the oracle contamination court.
Phase 15 — four-consumer drop-in status
Phase 15 drove the consumer drop-in push toward zero failures across the
four runtime consumers (lxml, php, nokogiri, debian) against the frozen
oracle. Snapshot (courts/receipts/phase-15/FOUR-CONSUMER-STATUS.md, plus
CURRENT-STATE.md / LXML-COURT-BASELINE.md):
- php — 0 failures (sealed in Phase 14); re-gated after every shared engine change (currently 1250/0).
- lxml — the candidate gap clustered in the XSLT/EXSLT engine and
validation surfaces; the oracle passes the full suite (2007 tests / 0
failures), the candidate is blocked on the Phase-16 push-parse item
above for its large-
iterparsetest. - nokogiri — the oracle passes its suite (2831 runs / 0 failures); candidate crash sites (XPath extension-function dispatch, SAX context creation) were isolated and fixed progressively.
- debian / drop-in — runtime substitution verified in the debian court container; ltrace-driven symbol analysis drove the missing-surface fills (xmlBuf API, legacy SAX1 handlers, exported global aliases, xz/encoding helpers) and the versioned-SONAME coexistence work.
Phase 14 — downstream custodian validation courts
Phase 14 built the consumer validation courts (lxml / Nokogiri / PHP /
Debian inside minimal docker VMs) and drove the PHP six-extension gate from
321 failures to zero (NTS and ZTS) — DOM (classic + modern),
SimpleXML, XML expat-compat, XMLReader, XMLWriter and XSL, family by family
(receipts courts/receipts/phase-14/php-14-NN-*/; the ZTS seal required the
R-000177 cross-DSO loader-slot bridge so the whole-archive libxslt facade
sees the streams loader PHP registers through the core). Green-gate
configuration: 1290 tests / 1250 passed / 40 skipped / 0 failed, identical
file list on both sides. The frozen-oracle court image
(libxml-rs/phase14-debian, libxml2 2.15.3 + libxslt 1.1.45) and the
consumer source trees became the substrate every later phase gates on.
Phase 11 (historical)
Phase 11 delivers the cross-version archaeology of §41 (historical oracle matrix), §42 (version fingerprints), §51 (multi-version triangulation). Per §85:
Deliverable: The project can explain how current behavior came to exist.
A configurable oracle matrix (oracle/historical/) builds 12 historical libxml2
releases (2.7.8 → 2.15.0) plus 5 libxslt releases (1.1.26 → 1.1.42) from the archaeology
git clones and runs a 28-case behavioral corpus against every oracle and the system
2.15.3/1.1.45 binaries, capturing byte-exact stdout/stderr/exit and per-version sha256
fingerprints:
oracle/historical/build.sh— era-tolerant oracle builder (era tag spellings, autotools modernizations,--without-threadsfor modern glibc)oracle/historical/run_matrix.sh— matrix runner + per-case epoch grouping;results/matrix.jsonholds version→case→sha256atlas/SEMANTIC_EPOCHS.md— the epoch map: 10 behavioral epochs (E-001…E-008 + exit-code epochs) correlated with the exact upstream commits/NEWS that created themcourts/suites/historical/HIST-EPOCH-*.json— §43 casefiles;courts/receipts/historical-matrix-2026-08-29.json— §44 receipt
Headline findings (all correlated to upstream commits in the atlas):
xmllint --xpathnode-set output gained newline separators in 2.9.10 (commitda35eeae, an upstream-documented breaking change)- The 2.9.10 parser regression (
EndTag: '</' not found) was fixed in 2.9.11 (de5b624f); the second parse-error diagnostic was dropped entirely in 2.12 - A 2.13.0 cluster: parser/validation exit codes reworked (1→4, 4→3), entity-in-attribute errors reported twice, entity-decl debug nodes became
TEXT compact(8d04f0ee) - 2.15.0:
--htmldumps became single-line (newline writes removed fromHTMLtree.c);--validwith no DTD returns exit 0 - libxslt core transform output is a stable epoch — byte-identical from 1.1.26 (2009) to 1.1.45
Phase 11.1 (historical)
Phase 11.1 (11.1-A … 11.1-Z) sealed the forensic surface census and parity
closure, and the 11.1-Z.1 evidence/packaging amendment (0.1.0-alpha.34)
sealed the three-DSO ELF contract and the residual-ledger corrections.
11.1-Z.2 (0.1.0-alpha.35) added the function-signature ABI plane: the
ABI-FUNCTION-SIGNATURE court mirrors every export across the oracle
header / candidate header / actual Rust extern "C" signature (3259
compared, 0 findings) and caught a whole defect class — the allocator hooks
(xmlMemSetup/xmlGcMemSetup missing mallocAtomicFunc and the int
return, two sources of allocator truth), shifted register layouts
(xmlC14NExecute, xmlAutomataNewCountTrans) and 20+ stale pre-2.10
signatures. The ALLOCATOR-HOOK differential court proves the merged
single-source-of-truth allocator byte-identical with the oracle. The
CUSTODIAN-COMMENTARY-DRIFT court pins every residual/court/epoch/
receipt reference in source commentary and bans embedded mutable counts.
The DSO-STATE-COHERENCE court pins a documented bounded divergence
(R-000177): the whole-archive libxslt/libexslt facades are the only
consumer-linkable construction, so they carry private copies of the core
state — hooks/globals installed through one DSO are not observed by the
others (the ELF contract itself — SONAMEs, NEEDED chains, export surfaces —
is verified by DSO-LOADER).
11.1-Z.3 (0.1.0-alpha.36) closed the allocator-UB and proof-scope
accounts. The default allocator was invalid-layout UB (Rust std::alloc
with fabricated Layouts); it is now plain libc malloc/realloc/free/
strdup (upstream 2.15.0 globals.c defaults), untracked exactly like the
oracle, with the debug-named surface (xmlMemMalloc/*Loc) keeping the
registry — R-000178, proven by the new ALLOCATOR-DEFAULT-001 differential
court (byte-identical incl. xmlMemSize/xmlMemUsed/xmlMemBlocks
exactness). The ABI-FUNCTION-SIGNATURE court was hardened to be
fail-closed and oracle-isolated: fully separated compile environments with
per-include-origin contamination checking, zero silent omissions (every
header and every declaration accounted), and two fingerprints —
SOURCE_PROTOTYPE (canonical base, pointer depth, pointee const/restrict,
typedef identity, FULL fn-pointer signatures) and MACHINE_ABI (class, width,
signedness, depth) — which found 25+ real candidate divergences that the
old lossy normalization could not see: signedness (xmlBufferGrow/
xmlDictSetLimit/xmlOutputBufferGetSize), wrong arities
(xmlValidateAttributeDecl 4→3 args), wrong returns (xmlXPathCastBooleanToNumber
int→double, xmlCheckVersion int→void, xmlInitThreads int→void), wrong
fn-pointer typedefs (xsltPreComputeFunction/xsltTopLevelFunction), an
invented xmlTextReaderGetAttributeIndex API (removed), char/xmlChar
pointee drifts, and the xmlAttributeTable typedef identities. The
CUSTODIAN-COMMENTARY-DRIFT census now GATES the verdict on zero
unaccounted safety sites via proof scopes: 1811/1811 unsafe functions and
5235/5235 unsafe blocks accounted (local proof 2514, enclosing proof scope
2720, classified-generated 1), plus the rustdoc gate
(RUSTDOCFLAGS="-D warnings" cargo doc), and the xslHandleDebugger
non-obligation is reclassified HEADER_DECLARED_ORACLE_DSO_ABSENT (the
oracle DSO itself does not export it).
11.1-W (generated parity matrix) replaced every hand-typed
headline count with evidence: tools/evidence/generate_all.py regenerates all
six canonical ledgers (atlas/PARITY_MATRIX.json, atlas/SURFACE_RECONCILIATION.json,
atlas/API_PARITY_LEDGER.json, atlas/ABI_PARITY_LEDGER.json,
atlas/HISTORICAL_SURFACE_EPOCHS.json, atlas/PARITY_OBLIGATIONS.json) and
their Markdown views, --check proves byte-reproducibility, and
tools/evidence/readme_counts.py generates the Project Status and
test-coverage tables in this README. The obligations ledger now covers all
three oracle projects (libxml2 2.15.3, libxslt 1.1.45, libexslt 0.8.25):
0 missing across 1683 obligations, with the residual ledger at
96 FIXED / 4 OPEN (100 residuals; the OPEN entries are R-000157
UNRESOLVED — iconv/ICU-only encodings, a real executed-platform gap pending
an iconv/ICU backend — R-000168, the unexecuted-platform obligation,
R-000177, the cross-DSO state partitioning of the facades (a deliberately-
open Phase-12 architectural target), and R-000179, the versioned-distro
binary contract gap).
The 11.1-Z.1 amendment shipped three real ELF DSOs (core libxml2.so.16 +
post-link facades libxslt.so.1 and libexslt.so.0 with the upstream NEEDED
chain), fixed the parity-matrix per-project DSO accounting, bound the
generated-evidence input identities (Doxygen inventories + headers), corrected
the verification-ladder state machine, and added the real
PREPROCESSOR-SURFACE / AST-SURFACE courts.
Phase 12 (real downstream substitution)
Phase 12 proved the ecosystem cannot tell the custodian implementation apart
from upstream — the Phase-11 seal is a 1c35e4e1…-rooted functional tree
(commits fed3cd03 and d2f72696, crate 0.1.0-alpha.37):
- EXPORT-SURFACE-DISPOSITION: every candidate DSO export classified
CURRENT_ORACLE_EXPORT / HISTORICAL_COMPAT_EXPORT / CUSTODIAN_EXTENSION /
INTERNAL_LEAK; the shipped
libxml2.so.16is the exact executed-oracle surface (version scripts hide the leaks), with the staticlib kept as the full implementation surface. - ELF-VERSIONING + BINARY-SUBSTITUTION (14/14): the upstream
LIBXML2_1.xnamed-version graph (27 nodes) forlibxslt.so.1with the oracle's per-symbol node assignment; consumers compiled and linked against the ORIGINAL oracle DSOs run unchanged against the candidate DSOs via runtime substitution — DT_VERNEED satisfied, no ld.so warnings. - DYNSYM-SURFACE (12/12) and DSO-BOUNDARY-LINT: the loader-visible surface is the intended contract and nothing escaped from the Rust implementation; the xslt layer may only reach libxml2 state through the sanctioned public ABI gateway (R-000177 machine lint).
- STATIC-SUBSTITUTION (13/13) and DOCKER-SUBSTITUTION (17/17, inside a minimal VM): static-linked and containerized unmodified external C consumers (xmllint/xsltproc builds, testWriter.c-style sources) link and run byte-identically.
- EXTERNAL-CONSUMERS (15/15): pkg-config/xml2-config/xslt-config substitution for downstream build systems.
Phase 13 (hostile audit)
Phase 13 attacked the candidate with NULLs, extremes and UB-adjacent defined
behavior, court by court, requiring byte-identical stdout/stderr/exit vs the
system oracle (probe receipts in courts/receipts/phase-13/):
- HOSTILE-ABI (72 NULL/extreme-size attacks): fixed real buffer/limits
divergences —
xmlReadMemoryINT_MAX wild-read rejection, upstream-faithfulxmlBufferCreate/CreateSize,xmlParseChunkNULL/negative → XML_ERR_ARGUMENT,buf_add/buf_add_headedge contract (R-000191). - HOSTILE-OWNERSHIP (O1–O12):
xmlNewNode/xmlNewPINULL-name rejection,create_int_subsetNULL-doc unattached DTD,utf8_strlen(NULL)→ -1 (R-000192). - HOSTILE-ALLOCATOR (H1–H6, size-based failure injection): real ownership
bug —
xmlStrcat/xmlStrncatnow free the old buffer on realloc failure (R-000193). - HOSTILE-CALLBACKS (C1–C10): real bugs —
xmlSAXUserParseMemory/Filecopy the caller's SAX into own storage (was freeing stack), return errNo, parent-input error-context fallback, I/O source-failure propagation (R-000194). - HOSTILE-FAILURE (F1–F10): missing
xmlRegexpPtr/xmlRegExecCtxtPtrtypedefs, streamed depth-limit error with source window, the legacy entity-loop "cur input" tail, XPath compile diagnostics with byte offsets,xmlParseDTDI/O warning, invalid-regexp → NULL (R-000195). - HOSTILE-THREADS (T1–T3): the error-handler slots and the other 14
TLS-era globals were GLOBAL data; upstream 2.15 keeps them thread-local
(
xmlGetThreadLocalStorage) and exports only the__xml*accessor FUNCTIONS. The candidate moved all 18 tothread_local!cells (src/xml/globals/tls.rs), removed the data-symbol exports and switched the headers to the upstream macro/accessor contract — a handler installed in one thread is no longer visible from another (R-000190). - HOSTILE-ORACLE-CONTAMINATION (dimension 7): readelf/nm/probe prove the candidate DSOs carry no system libxml2/libxslt/libexslt dependency and every symbol resolves inside the candidate directory.
The Phase-13 courts found and permanently courted each defect class; the hostile receipts, the TLS-globals model and the header contract changes are sealed with the Phase-13 commit.
Phase 10 (historical)
Phase 10 delivered the xmllint and xmlcatalog command-line tools
(§36):
Both tools are faithful native-Rust ports of the upstream programs, verified byte-for-byte against the system libxml2 2.15.3 binaries:
xmllintCLI —--debug,--copy,--format,--valid/--postvalid,--dtdvalid,--xpath/--xpath0,--xinclude,--html/--xmlout,--noent,--no-compact,--encode,--recover,--dropdtd,--pedantic,--noout,--quiet,--nonet,--huge, … with upstream exit codes (0/1/3/4) and the upstreamfile:line: parser error : MSG+ source-line/caret diagnosticsxmlcatalogCLI —--create,--add,--del,--resolve(system/public/URI),--noout,--shell(interactiveresolve/system/public/add/del/dump/help/exit), SGML→XML catalog conversion, upstream exit codes- Compact text nodes — the parser reproduces libxml2's
XML_PARSE_COMPACTinline text storage (≤15 bytes in the node struct), so--debugdumps showTEXT compactexactly like the oracle; merged/entity-interrupted text is correctly non-compact - DTD validation diagnostics —
--validoutput (messages, caret placement, exit codes) matches the oracle for both no-DTD and declaration errors - Entity expansion —
--noentre-parses declared-entity content through the input stack (nested references and markup entities included), matching upstream trees - HTML serialization — meta-charset insertion, upstream formatting rules (p/pre/param never formatted, single-child and inline elements inline), HTML document headers
- 1291 passing tests:
cargo test --lib— 0 failures - Differential oracle parity: a 44-case CLI suite (
target/difftest_summary.sh) is byte-identical to the system tools (stdout + stderr + exit codes), plus a 30+ case edge corpus (entities, DTDs, HTML, compact/no-compact, debug dumps, XPath)
Phase 9 (historical)
Phase 9 delivered the complete EXSLT module set (§35) and the
xsltproc command-line tool (§36):
All seven EXSLT modules are implemented natively and registered through the
process-wide EXSLT registry (exsltRegisterAll, mirroring upstream):
exsl:Common —exsl:node-set(with real result-tree-fragment support),exsl:object-typemath:—math:max,math:min,math:sin,math:cos,math:tan,math:constant,math:power,math:sqrt,math:log,math:random, …set:—set:difference,set:intersection,set:distinct,set:has-same-node,set:leading,set:trailingstr:—str:tokenize,str:padding,str:concat,str:split,str:replace,str:align,str:encode-uri,str:decode-uridyn:—dyn:evaluate,dyn:element,dyn:attribute,dyn:map,dyn:call, …date:—date:date-time,date:date,date:time,date:year,date:month-in-year,date:day-in-month,date:day-of-week-in-month,date:format-date,date:add,date:difference,date:seconds,date:day-name, …func:—func:function,func:result(extension-function declarations)exsltRegisterAllC ABI export — mirrors upstream;xsltproccalls it at startupxsltprocCLI — full option surface (--param,--stringparam,--output,--noout,--html,--encoding,--xinclude,--profile,--maxdepth,--maxvars,--nonet,--nowrite, …) with upstream exit codes (1–11)- RTF support — variables with inline content become context-owned result tree fragments;
exsl:node-set($var)/pathnavigation works - 1291 passing tests:
cargo test --lib— 0 failures - Differential oracle parity: a 12-case
xsltproccorpus (basic transform,count()/AVTs,exsl:node-set/math:/set:/str:, predicates, attribute string-values,xsl:if/xsl:when, numbering, descendingxsl:sort,key(),call-templatewith params,method="html") is byte-identical to the system libxslt 1.1.45xsltproc(stdout + exit codes)
Underlying subsystem fixes landed during Phase 9
| Fix | Surface | Detail |
|---|---|---|
| XPath core functions in XSLT | xslt | The transform context now registers the XPath 1.0 core function library — before, every XPath function call (count(), substring(), …) failed as unknown |
| AVT evaluation | xslt | {expr} attribute value templates evaluated in literal attributes and xsl:element/xsl:attribute/xsl:processing-instruction names (XSLT 1.0 §7.6.2) |
| RTF variable ownership | xslt | Inline variable content is deep-copied into a context-owned RVT (freed exactly once at context teardown) — fixes a double-free and enables exsl:node-set |
| Node string-value | tree | node_get_content concatenates all descendant text (XPath 1.0 string-value), not just direct text children |
| Caller parameter format | xslt | xsltApplyStylesheet params parsed as upstream (name, value) pairs with {uri}name namespace form |
date: no-arg default |
exslt | date:date()/date:time() and the component functions default to the current date-time (EXSLT spec) |
Phase 8 (historical)
Phase 8 implemented the complete native-Rust XSLT 1.0 engine: stylesheet compilation, template matching, pattern compilation, variable/parameter binding, keys, sorting, numbering, imports/includes, extensions, security, serialization, and the transform runtime (§31–§34). The XSLT engine operates exclusively on the Rust libxml implementation — no upstream libxml2/libxslt is loaded, linked, or shelled out to (§31):
Rust CLI → Rust libxslt compatibility layer → Rust XSLT engine
→ Rust XPath implementation → Rust libxml tree/parser/serializer
Subsystem fixes landed during Phase 8:
| Fix | Surface | Detail |
|---|---|---|
| SAX2 namespace resolution | parser | startElementNs receives split prefix/localname + resolved URIs; element and attribute namespaces now attached by the tree builder |
| Absolute path evaluation | XPath | /root/item evaluates from the document node, not the root element (XPath 1.0 / semantics) |
| XPath C ABI helpers | ABI | xmlXPathObjectCopy, xmlXPathCastToString, xmlXPathCastStringToNumber, xmlXPathCmpNodes, xmlXPathNodeSetCreate |
| Node content getter | tree | node_get_content (upstream xmlNodeGetContent semantics) |
| Template-content ownership | xslt | Template content belongs to the stylesheet document; xsltFreeTemplate no longer double-frees it |
node()/text() patterns |
xslt | Bare node-test calls translate to steps with correct §5.5 priorities |
match="/" semantics |
xslt | Matches only the document node, not the root element |
All six Phase 8 residuals are documented in atlas/RESIDUAL_LEDGER.md (R-000101–R-000106).
Test coverage by subsystem
| Subsystem | Tests |
|---|---|
| XPath 1.0 | 128 |
| XML parser + SAX | 113 |
| Encoding | 79 |
| URI | 70 |
| XML Schema (XSD) | 64 |
| DTD validation | 56 |
| RELAX NG | 56 |
| XSLT patterns | 46 |
| I/O | 45 |
| Regex | 44 |
| Schematron | 40 |
| C14N | 39 |
| XML Reader | 38 |
| Tree/ownership | 36 |
| DTD | 35 |
| HTML | 33 |
| Entities | 31 |
| XSLT transform | 27 |
| XInclude | 23 |
| Catalog | 22 |
| XML Writer | 20 |
| Automata | 16 |
| XPointer | 15 |
| XSLT numbering | 14 |
| ABI allocator | 13 |
| Debug | 11 |
| Char validation | 9 |
| Compatibility profiles | 9 |
| EXSLT dates | 9 |
| Hash | 9 |
| ABI (xml2 exports) | 8 |
| ABI (xslt exports) | 8 |
| List | 8 |
| Dictionary | 7 |
| EXSLT saxon | 7 |
| Globals | 7 |
| Serialization | 7 |
| EXSLT strings | 6 |
| String | 6 |
| EXSLT math | 5 |
| Errors | 5 |
| XSLT compiler | 5 |
| XSLT params | 5 |
| XSLT security | 5 |
| EXSLT common | 4 |
| EXSLT sets | 4 |
| XSLT serialization | 4 |
| XSLT stylesheet | 4 |
| XSLT variables/params | 4 |
| EXSLT registry | 3 |
| XSLT sorting | 3 |
| ABI data globals | 2 |
| EXSLT dynamic | 2 |
| Memory | 2 |
| Threads | 2 |
| XSLT extensions | 2 |
| XSLT keys | 2 |
| XSLT namespace alias | 2 |
| XSLT whitespace | 2 |
| abi::exports_html | 2 |
| abi::structs | 2 |
| EXSLT functions | 1 |
| XSLT documents | 1 |
| XSLT imports | 1 |
| XSLT misc (attrs) | 1 |
| abi::exports_parser | 1 |
| fuzz::fuzz_html_read_memory_no_panic | 1 |
| fuzz::fuzz_xml_read_memory_no_panic | 1 |
| Total (1291 passing, 0 failed) |
Project Structure
libxml-rs/
├── Cargo.toml # Single Cargo package (no workspace)
├── src/
│ ├── lib.rs # Library entry point
│ ├── abi/ # C ABI compatibility layer (§4, §14)
│ ├── xml/ # libxml2 implementation (§1, §3, §31)
│ │ └── parser/ # scanner (scalar/AVX2/AVX-512 §16.7), tokenizer, state
│ ├── xslt/ # libxslt implementation (§31–§34)
│ ├── exslt/ # EXSLT modules (§35)
│ ├── compatibility/ # Historical profiles, quirks, platform (§68, §69)
│ ├── internal/ # Internal helpers
│ └── bin/ # CLI tools: xmllint, xmlcatalog, xsltproc (§36)
├── include/ # Compatible C headers (§15)
├── benches/ # Criterion microbenchmarks (§16.2)
├── examples/ # ABI-mirror / allocator probe measurements
├── atlas/ # Forensic archive (§7–§12) + generated evidence
│ ├── PARITY_MATRIX.md # Current parity status
│ ├── RESIDUAL_LEDGER.md # Unexplained differences
│ ├── PERFORMANCE_ATLAS.md # Phase-16 performance evidence spec
│ └── … # release/api/abi/symbols/config inventories, epochs
├── oracle/ # Reproducible upstream build environment (§39)
├── courts/ # Differential testing framework (§40–§50)
│ ├── suites/ # Court suites incl. phase14 (consumers) + phase16
│ ├── receipts/ # Execution receipts (phase-14/15/16 …)
│ └── cli/ # xmllint/xmlcatalog/xsltproc CLI differential runner
├── tools/
│ ├── bench/ # Provider-isolated Pareto harness + docker court (§16.2)
│ ├── evidence/ # Generated-ledger/README-count generators
│ ├── packaging/ # facade-gen.sh (whole-archive libxslt/libexslt),
│ │ # linker wrapper, version-script profiles
│ └── … # archaeology, courts
├── fuzz/ # cargo-fuzz targets (parse/html/xpath; ASan)
├── docker/ # Reproducible Docker oracle images
├── docs/ # Technical documentation
└── archaeology/ # Upstream git clones (immutable, offline)
Build
# Test C consumer compilation against our headers:
# Release packaging: build the core, then regenerate the whole-archive
# libxslt.so.1/libexslt.so.0 facades (always re-run after core edits — the
# facade link is timestamp-skipped by cargo):
# Build the frozen-oracle court image (libxml2 2.15.3 + libxslt 1.1.45):
The crate builds as cdylib + staticlib + rlib; the build script also
emits libxml-2.0.pc/libxslt.pc pkg-config files, xml2-config/xslt-config
scripts, and SONAME symlinks into the target directory.
License
Licensed under either of:
- MIT license (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)
at your option.
Project Status
| Dimension | Status |
|---|---|
| API completeness | libxml2 1395 oracle functions, 1395 fully reconciled; libxslt 232/232 reconciled; libexslt 13 oracle functions (evidence: atlas/PARITY_MATRIX.json, atlas/API_PARITY_LEDGER.json) |
| ABI compatibility | 0 mismatches across 937 measured entities (struct/enum layouts), verdict PASS (evidence: atlas/ABI_PARITY_LEDGER.json) |
| Parity obligations | 1683 obligations; 0 missing, 292 parity-verified by per-symbol courts (evidence: atlas/PARITY_OBLIGATIONS.json) |
| Subsystem census | 85 subsystems classified; verdicts: IMPLEMENTED_UNVERIFIED 43, PARTIAL 42 (evidence: atlas/SUBSYSTEM_CENSUS.json) |
| Surface reconciliation | libxml2: doxygen 1374 / AST 1403 / DSO 1395 functions; libxslt: 235 / 231 / 232 (evidence: atlas/SURFACE_RECONCILIATION.json) |
| Historical surface epochs | libxml2 2785 entities across 11 boundaries (evidence: atlas/HISTORICAL_SURFACE_EPOCHS.json) |
| Test coverage | 1291 passing, 0 failed, 1 ignored (cargo test --lib, evidence: atlas/TEST_COUNTS.json) |
| C headers | gcc & clang header-compile courts green (596/596, evidence: courts/receipts/header-compile-*) |
| CLI parity | xmllint + xmlcatalog + xsltproc differential oracle parity (evidence: courts/receipts/CLI-*) |
| Oracle infrastructure | 12 historical libxml2 + 5 libxslt oracles + system 2.15.3/1.1.45/0.8.25 oracles; evidence: oracle/historical, atlas/DOXYGEN_SURFACE_ATLAS.json |
| Downstream testing | Phase 12 sealed: EXTERNAL-CONSUMERS 15/15, ELF-VERSIONING + BINARY-SUBSTITUTION 14/14, DYNSYM-SURFACE 12/12, STATIC-SUBSTITUTION 13/13, DOCKER-SUBSTITUTION 17/17, EXPORT-SURFACE-DISPOSITION (evidence: courts/receipts/phase-12/) |
| Hostile audit | Phase 13 sealed: HOSTILE-ABI / OWNERSHIP / ALLOCATOR / CALLBACKS / FAILURE / THREADS / ORACLE-CONTAMINATION differential courts byte-identical vs the system oracle (evidence: courts/receipts/phase-13/) |
See atlas/PARITY_MATRIX.md for the detailed,
evidence-bounded parity matrix and atlas/RESIDUAL_LEDGER.md
for the residual ledger.