LEPTRIS

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

modelAPImemoryuse when
DOM (tree)leptris_parse_stringwhole documentRandom access, XPath, mutation, serialization. The default — everything else is an optimization.
SAX (push)leptris_sax_parse / feedbounded by depthThe 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_filebounded by the input sliceYou 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_filebounded by the largest subtreeYou 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.