One engine, four ways to consume XML
DOM for random access, SAX for the cheapest push events, pull (StAX-style) for binding-friendly streaming, iterparse for tree-shaped processing of huge files.
The decision table
| model | API | memory | use when |
|---|---|---|---|
| DOM (tree) | leptris_parse_string | whole document | Random access, XPath, mutation, serialization. The default — everything else is an optimization. |
| SAX (push) | leptris_sax_parse / feed | bounded by depth | The engine calls you as it parses. Cheapest per event — but every callback crosses the FFI boundary (~1 µs each through bindings). |
| Pull (StAX-style) | leptris_pull_new / _new_file | bounded by the input slice | You call the engine: leptris_pull_next() returns events on demand. Same streaming guarantees as SAX, zero C→host callbacks — the binding-friendly form of streaming. |
| Iterparse (incremental) | leptris_iterparse_new / _new_file | bounded by the largest subtree | You want a tree but not the whole document at once: each top-level child materializes in its own pool and is released when you move on. |
Pull (StAX-style)
c
LeptrisPullParser p = leptris_pull_new(xml, len);
const LeptrisPullEvent* ev;
while ((ev = leptris_pull_next(p)) != NULL) {
if (ev->type == LEPTRIS_PULL_START_ELEMENT)
handle(leptris_pull_attr_count(p), ev->name);
if (ev->type == LEPTRIS_PULL_ERROR) break;
}
leptris_pull_free(p);Iterparse
c
LeptrisIterparse it = leptris_iterparse_new_file("huge.xml");
LeptrisElement e;
while ((e = leptris_iterparse_next(it)) != NULL)
process(e); /* valid until the next call */
leptris_iterparse_free(it);Iterparse v1 note: element names are the QNames as written; namespace prefixes are not re-resolved — use the DOM path when namespace URIs matter.
This is the curated guide. The repositories are canonical: when this page and the repo disagree, the repo wins. Full documentation lives with the source.