rusty_xml
rusty_xml is a ground-up, pure-Rust remake of libxml2: well-formed XML 1.0 parse, arena DOM, SAX2, pull reader, writer/save, XPath 1.0, DTD, C14N, HTML, and working subsets of RelaxNG / XSD / Schematron.
#![forbid(unsafe_code)]on every published crate, no C, nolibxml2-sys, no copyleft. Defaults areXML_PARSE_NONET | XML_PARSE_NO_XXE— the safe posture libxml2's own README says the C library does not have.
Part of Remade With Rust by Mata Network — the XML toolkit for the stack that already ships rusty_zstd, rusty_h264, and remade_ffmpeg_rs. Jump to the ecosystem ↓
The headline
A pure-safe-Rust XML 1.0 toolkit that is a reimplementation, not a
wrapper, with libxml2 function names as #[doc(alias)] and safe defaults
C historically got wrong:
- Parse: UTF-8 well-formed documents, 15 built-in 8-bit encodings (no
iconv), push (
xmlParseChunk), IO callbacks, local OASIS catalogs. SAX traces are gated line-for-line against pinnedxmllint --sax. - Tree / save / writer / reader: arena DOM,
xmlsave(includingXML_SAVE_NO_EMPTY/NO_DECL),xmlTextWriter,xmlTextReader. Empty elements are one readerElement, no extraEndElement.parse(write(parse(x)))is a standing gate. - XPath 1.0 compile + eval;
rxmlint --xpathprints xmllint form. - Validate / canonicalize: DTD internal subset + default attributes,
xmlValidateDocument, C14N 1.0 and exclusive, XInclude via a caller loader. - HTML (
HTMLparser.cgrammar, implied html/head/body) and working subsets of RelaxNG, XML Schema, and Schematron. - CLI is
rxmlint, neverxmllint. Same flag language so a bench script can swap argv[0]. - The C oracle is an external process. We never link libxml2. Pin:
libxml2 v2.15.3 (
oracle/PIN).
| libxml2 (C) | rusty_xml (Rust) | |
|---|---|---|
| C/C++ in the dependency tree | all of it | none — no libxml2-sys, no iconv, no zlib-sys |
unsafe in the published crates |
extensive | 0 — #![forbid(unsafe_code)] |
| License | MIT | MIT OR Apache-2.0 |
| Defaults on untrusted input | libxml2 README: not recommended | NONET | NO_XXE, always |
| CLI name | xmllint |
rxmlint (does not shadow C) |
| Network entity loads | historically on | off unless you pass flags (and the parser still ORs NONET) |
Performance (session, not a publish claim)
First paired board vs pinned libxml2 v2.15.3 xmllint, slashdot.xml
(3675 bytes, 101 elements / 403 reader ticks), 100000 inner parses,
N=6 pairs, CPU time, one core (not 0), High priority, ABBA. us/C > 1
means C is faster. Full method line in
bench/SIDE-BY-SIDE.md.
| workload | rusty_xml | libxml2 | us/C | wins (us) |
|---|---|---|---|---|
parse-noout (DOM, discard) |
6.1 MB/s | 33.6 MB/s | 5.5× | 0/12, z = −3.46 |
stream-noout (reader) |
5.7 MB/s | 33.1 MB/s | 5.8× | 0/12, z = −3.46 |
Read this as: correctness is gated; speed is the open work (M7).
N=6 is a session, not publish (that is N≥20). C's arm was under the ~15 s
duration floor (DURATION_SHORT). Do not quote these cells as "faster than
libxml2" — they are the opposite, and that is fine: the brick that closes
the gap has not shipped yet. C default flags are
XML_PARSE_COMPACT \| XML_PARSE_BIG_LINES plus xmlCtxtReadFile per
--repeat (Windows pin has no mmap); us is xml_read_memory /
xml_reader_for_memory after one fs::read, with NONET \| NO_XXE.
That is CLI-vs-CLI.
What is this?
rusty_xml is libxml2 remade in Rust. Unlike
libxml2-sys / quick-xml /
roxmltree — bindings or different grammars — there is no C in the
dependency tree here and the public names match C (xmlReadMemory →
xml_read_memory, documented with #[doc(alias)]).
libxml2's own README says it is not recommended for untrusted data. That is the defect this remake exists to close: semantic identity under matched options, safe defaults (no network, no XXE, bounded amplification).
It is a reimplementation of the algorithms, not a fork. The C sources are
neither distributed nor linked; a pinned xmllint is used only as an
external-process oracle (scripts/fetch-oracle.ps1).
cargo-deny enforces the promise: no *-sys crate, no copyleft, and no
libxml2-sys anywhere in the graph.
The Remade With Rust ecosystem
Remade With Rust is an initiative by Mata Network to rebuild essential C and C++ tools in Rust — for the memory safety, the predictable performance, and the freedom of a permissive license. Each project is a reimplementation, not a fork: same wire protocols and file formats, new code you can actually depend on.
We build the core to production grade and open-source it so the community can extend it. No copyleft. No surprises. Just the tools we rely on, made faster and safer.
| Project | What it is |
|---|---|
| 🎬 remade_ffmpeg_rs | Our FFmpeg alternative. Drop-in ffmpeg and ffprobe binaries — demux → decode → filter → encode → mux, rebuilt as composable Rust crates with zero GPL/LGPL. Apache-2.0. |
| 🧠 FFAI | Our sister project: media for AI. "The AI media toolkit, remade with rust." Embedded ASR + TTS (Mercury), OCR (Carmenta) and vision-language captioning (Argus) behind an ffmpeg-style, swap-by-name architecture — no Python, no CUDA. MIT OR Apache-2.0. |
| 🌐 Mata Network | The home page. "Stop sacrificing your privacy for convenience." Sovereign, self-hostable privacy infrastructure — wallet & identity, password manager, contact manager, and a browser extension that stops information leaking as you browse. Remade With Rust is its open-source arm. |
→ All projects: github.com/Remade-With-Rust
Install
One crate — rusty_xml — is the public facade; it re-exports parser, tree,
SAX, reader, writer, XPath, and validation. Add it with:
or in Cargo.toml:
[]
= "0.1"
MSRV is 1.85. The library never sets #[global_allocator].
The published crates (all 0.1, MIT OR Apache-2.0):
| Crate | Role | Docs |
|---|---|---|
rusty_xml |
the facade — depend on this | docs.rs |
rusty_xml-parser |
well-formed parse, encodings, push, catalogs, HTML | docs.rs |
rusty_xml-tree |
arena DOM | docs.rs |
rusty_xml-sax |
SAX2 recorder + xmllint-debug dump | docs.rs |
rusty_xml-reader |
xmlTextReader |
docs.rs |
rusty_xml-writer |
xmlsave + xmlTextWriter |
docs.rs |
rusty_xml-xpath |
XPath 1.0 | docs.rs |
rusty_xml-valid |
DTD, C14N, RelaxNG, XSD, Schematron | docs.rs |
rusty_xml-cli |
rxmlint binary |
— |
Not published: rusty_xml-bench (oracle harness), rusty_xml-c-abi (M8 stub),
rusty_xml-alloc (binary allocator seam — never used by the library).
Dropping it into a downstream tool: depend on the facade. Call
xml_read_memory / xml_reader_for_memory / xml_xpath_eval. Do not add
libxml2-sys. Safe defaults are already on; you do not opt into NONET.
Quick start
use ;
XPath 1.0 on the same tree:
use ;
Pull reader:
use ;
Command-line (never installs as xmllint):
Architecture
The workspace mirrors libxml2's headers, not its build:
crates/
rusty_xml public facade ← depend on this
rusty_xml-parser parser.h — well-formed, encodings, push, catalogs, HTML
rusty_xml-tree tree.h — arena DOM
rusty_xml-sax SAX2.h — recorder + xmllint-debug dump
rusty_xml-reader xmlreader.h
rusty_xml-writer xmlsave.h + xmlwriter.h
rusty_xml-xpath xpath.h
rusty_xml-valid valid.h, c14n, RelaxNG / XSD / Schematron subsets
rusty_xml-cli rxmlint (not xmllint)
rusty_xml-c-abi optional cdylib, stub until M8. Not published.
rusty_xml-bench shells out to pinned xmllint. Never links libxml2.
rusty_xml-alloc rusty_alloc seam for binaries only. Not published.
bench/ codec-measurement harness (pinvs.ps1)
oracle/PIN libxml2 v2.15.3 pin (binary is gitignored)
Platform support
| Platform | Status |
|---|---|
| Windows (x86-64) | ✅ builds + tests |
| Linux | ✅ builds + tests |
| macOS | ✅ builds + tests |
wasm32-unknown-unknown |
✅ library cargo check in CI |
No C toolchain, no iconv, no nasm. Gzip (XML_PARSE_UNZIP) is not wired yet
(a 1f 8b buffer is an error). ISO-2022-JP / Shift_JIS / EUC-JP are
unsupported, matching libxml2 built without iconv.
Roadmap
- M0 — pin oracle (libxml2 v2.15.3), C-only board, workspace skeleton
- M1 — character classes, UTF-8 well-formed parse, SAX-exact vs
xmllint --sax - M2 — tree mutation,
xmlsave,xmlTextWriter,xmlTextReader, round-trip - M3 — encodings without iconv, push parser, IO callbacks, local catalogs
- M4 — XPath 1.0 compile + eval,
rxmlint --xpath - M5 — DTD validation, C14N 1.0 + exclusive, XInclude (loader-gated)
- M6 — HTML parser, RelaxNG / XSD / Schematron working subsets
- M7 — performance campaign vs pinned
xmllint(parse-noout/stream-noout) - M8 — C ABI
cdylib(XMLPUBFUNnames) + hardening audit - Optional gzip (
miniz_oxide/XML_PARSE_UNZIP) - Full xmlconf / full RelaxNG corpora (not only the working-subset fixtures)
Plan: docs/plan/rusty_xml.md.
License
MIT OR Apache-2.0, at your option — see LICENSE-MIT and
LICENSE-APACHE. No GPL/LGPL and no C anywhere in the
dependency tree, CI-enforced with cargo-deny. The C xmllint binary used
as a measurement oracle is neither distributed here nor linked; see
NOTICE.md.
About Mata Network
Mata Network builds sovereign, self-hostable privacy infrastructure — "stop sacrificing your privacy for convenience": wallet & identity, a password manager, a contact manager, and a browser extension that stops your information leaking as you browse.
Remade With Rust is our open-source home for the permissively-licensed building blocks that work depends on — including remade_ffmpeg_rs (the FFmpeg alternative) and FFAI (the AI media toolkit).