libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_stream.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License.
4 *
5 * src/inc/xtc_stream.h
6 * Async streams: a lazy, pull-based sequence of values with
7 * composable combinators (map, filter, for_each). A stream is
8 * nothing more than a next() function that produces the next value
9 * (or signals end-of-stream), plus a small vtable so combinators can
10 * wrap one stream in another without materializing the whole
11 * sequence -- the Elixir Stream / Rust Iterator shape.
12 *
13 * Pull-based composes naturally with the demand channel (R9): a
14 * consumer pulls one value at a time, so a slow map/filter stage
15 * exerts backpressure just by pulling more slowly. Use
16 * xtc_stream_from_demand to turn an xtc_chan_demand into a stream.
17 *
18 * Values are void*; the stream never owns them (it neither copies
19 * nor frees). A combinator's transform decides ownership.
20 */
21
22#ifndef XTC_STREAM_H
23#define XTC_STREAM_H
24
25#include <stddef.h>
26
27#include "xtc.h"
28#include "xtc_chan.h"
29
30typedef struct xtc_stream xtc_stream_t;
31
32/*
33 * Pull one value. On XTC_OK, *out holds the next value. Returns
34 * XTC_E_NOTFOUND at end-of-stream, XTC_E_AGAIN if no value is available
35 * yet but the stream is not finished (the caller should retry, e.g.
36 * after a demand-channel item arrives), or another XTC_E_* on error.
37 */
38typedef int (*xtc_stream_next_fn)(void *ctx, void **out);
39
40/*
41 * PUBLIC: int xtc_stream_create __P((xtc_stream_next_fn, void *, xtc_stream_t **));
42 * PUBLIC: void xtc_stream_destroy __P((xtc_stream_t *));
43 * PUBLIC: int xtc_stream_next __P((xtc_stream_t *, void **));
44 * PUBLIC: int xtc_stream_map __P((xtc_stream_t *, void *(*)(void *, void *), void *, xtc_stream_t **));
45 * PUBLIC: int xtc_stream_filter __P((xtc_stream_t *, int (*)(void *, void *), void *, xtc_stream_t **));
46 * PUBLIC: int xtc_stream_for_each __P((xtc_stream_t *, int (*)(void *, void *), void *));
47 * PUBLIC: int xtc_stream_from_demand __P((xtc_chan_demand_t *, xtc_stream_t **));
48 */
49
50/* Create a stream from a next() function and its context. */
51int xtc_stream_create(xtc_stream_next_fn next, void *ctx,
52 xtc_stream_t **out);
53
54/* Destroy a stream. Destroying a combinator stream (map/filter) also
55 * destroys the source stream it wraps, recursively; destroy only the
56 * outermost stream. Does not touch the underlying demand channel of a
57 * from_demand stream (the caller owns that). */
58void xtc_stream_destroy(xtc_stream_t *s);
59
60/* Pull the next value. See xtc_stream_next_fn for the return codes. */
61int xtc_stream_next(xtc_stream_t *s, void **out);
62
63/* map: each pulled value v becomes fn(v, user). Lazy: fn runs only when
64 * the resulting stream is pulled. The new stream OWNS `s`. */
65int xtc_stream_map(xtc_stream_t *s,
66 void *(*fn)(void *v, void *user), void *user,
67 xtc_stream_t **out);
68
69/* filter: values for which pred(v, user) returns 0 are skipped. The new
70 * stream OWNS `s`. */
71int xtc_stream_filter(xtc_stream_t *s,
72 int (*pred)(void *v, void *user), void *user,
73 xtc_stream_t **out);
74
75/* Drain the stream, calling fn(v, user) for each value until
76 * end-of-stream. A nonzero fn return stops early and is returned. An
77 * XTC_E_AGAIN from the source is treated as "retry": for_each spins on
78 * it, so use it only with a source that will eventually produce or
79 * finish (e.g. a fully-buffered demand channel that has been closed). */
80int xtc_stream_for_each(xtc_stream_t *s,
81 int (*fn)(void *v, void *user), void *user);
82
83/* Adapt a demand channel into a stream: each pull grants one unit of
84 * demand and returns the next buffered item (XTC_E_AGAIN until one
85 * arrives, XTC_E_NOTFOUND once the channel is closed and drained). */
86int xtc_stream_from_demand(xtc_chan_demand_t *ch, xtc_stream_t **out);
87
88#endif /* XTC_STREAM_H */