Skip to content

Async I/O

The tokio feature exposes parse_async and friends, which accept any tokio::io::AsyncRead + Unpin source and parse without blocking the runtime thread.

Enabling

[dependencies]
sup-xml = { version = "*", features = ["tokio"] }
tokio = { version = "1", features = ["rt", "io-util"] }

Parse from any AsyncRead

use sup_xml::async_io::parse_async;
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let bytes: &[u8] = b"<r><b/></r>"; // `&[u8]` implements `AsyncRead`
let doc = parse_async(bytes).await?;
# Ok(())
# }

Any AsyncRead works — a tokio::net::TcpStream, a tokio::fs::File (with the fs tokio feature), the body of an axum / hyper request, an S3 byte-stream, a chunked HTTP response, etc.

Custom options

use sup_xml::{async_io::parse_async_with, ParseOptions};
# async fn run(file: tokio::fs::File) -> Result<(), Box<dyn std::error::Error>> {
let opts = ParseOptions {
recovery_mode: true,
skip_inter_element_whitespace: true,
..Default::default()
};
let doc = sup_xml::async_io::parse_async_with(file, &opts).await?;
# Ok(())
# }

Streaming and untrusted input

For arbitrarily large payloads, cap the reader before handing it to parse_async so a malicious upstream can’t OOM your process:

use tokio::io::AsyncReadExt;
# async fn handler(body: impl tokio::io::AsyncRead + Unpin) -> Result<sup_xml::Document, Box<dyn std::error::Error>> {
const MAX_BODY: u64 = 10 * 1024 * 1024;
let capped = body.take(MAX_BODY);
let doc = sup_xml::async_io::parse_async(capped).await?;
# Ok(doc)
# }

parse_async reads the entire stream into a buffer before invoking the sync parser — XML is not a streamable grammar in the general case (a late <!DOCTYPE> can change the meaning of earlier entities). For genuinely streaming use cases where the document IS line-delimited or NDJSON-style, drive XmlBytesReader directly from the parsing guide.

Threading model

parse_async is Send-safe and works under any tokio runtime (multi-thread or current-thread). The underlying parser is synchronous once the bytes are buffered — async only governs how the bytes arrive. If your input is local memory, prefer the sync parse_str / parse_bytes; the async wrapper is for cases where the bytes are remote and reading them blocks your thread.