xtc_stream(3)

---

xtc_stream(3)

lazy pull-based async streams with combinators

XTC_STREAM(3) Library Functions Manual XTC_STREAM(3)

xtc_stream_create, xtc_stream_destroy, xtc_stream_next, xtc_stream_map, xtc_stream_filter, xtc_stream_for_each, xtc_stream_from_demandlazy pull-based async streams with combinators

library “libxtc”

#include <xtc_stream.h>

int
xtc_stream_create(xtc_stream_next_fn next, void *ctx, xtc_stream_t **out);

void
xtc_stream_destroy(xtc_stream_t *s);

int
xtc_stream_next(xtc_stream_t *s, void **out);

int
xtc_stream_map(xtc_stream_t *s, void *(*fn)(void *, void *), void *user, xtc_stream_t **out);

int
xtc_stream_filter(xtc_stream_t *s, int (*pred)(void *, void *), void *user, xtc_stream_t **out);

int
xtc_stream_for_each(xtc_stream_t *s, int (*fn)(void *, void *), void *user);

int
xtc_stream_from_demand(xtc_chan_demand_t *ch, xtc_stream_t **out);

An async stream is a lazy, pull-based sequence of values -- the Elixir ‘Stream’ and Rust ‘Iterator’ shape. A stream is just a () function that yields the next value or signals end-of-stream, wrapped so combinators can layer transformations without materializing the whole sequence. Pull-based composition means a slow downstream stage exerts backpressure simply by pulling more slowly, which pairs directly with the demand channel (xtc_chan(3)).

Values are void *; the stream never copies or frees them. A combinator's transform decides ownership.

() builds a base stream from a next() function and its context. () pulls one value: it writes *out and returns XTC_OK, XTC_E_NOTFOUND at end-of-stream, XTC_E_AGAIN if no value is ready yet but the stream is not finished (retry), or another XTC_E_* on error.

() returns a stream in which each pulled value v becomes (v, user); () returns a stream that skips values for which (v, user) is 0. Both are lazy and both OWN the source stream, so destroying the combinator destroys the source; destroy only the outermost stream.

() drains a stream, calling (v, user) for each value until end-of-stream. A nonzero fn() return stops early and is returned; an XTC_E_AGAIN from the source is retried, so use xtc_stream_for_each() only with a source that will eventually produce or finish.

() adapts a demand channel into a stream: each pull grants one unit of demand and returns the next buffered item. It does not own the channel.

All functions except xtc_stream_destroy() return XTC_OK or a negative XTC_E_* code (see xtc_stream_next() for its stream-specific codes).

xtc_stream_destroy() returns no value; it recursively destroys any wrapped source stream but never touches the demand channel of a xtc_stream_from_demand() stream.

/* double the even numbers coming off a demand channel */
static int is_even(void *v, void *u) { (void)u; return (*(int*)v & 1)==0; }
static void *dbl(void *v, void *u) { (void)u; *(int*)v *= 2; return v; }
static int print_it(void *v, void *u) { (void)u; printf("%d\n",*(int*)v); return 0; }

xtc_stream_t *s, *f, *m;
xtc_stream_from_demand(ch, &s);
xtc_stream_filter(s, is_even, NULL, &f);
xtc_stream_map(f, dbl, NULL, &m);
xtc_stream_for_each(m, print_it, NULL);
xtc_stream_destroy(m);   /* frees m -> f -> s */

xtc_chan(3), xtc_launch(3), xtc(7)

July 10, 2026 Debian

View the mdoc source