1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Stack-allocated ring buffers for no-std embedded targets.
//!
//! # Primitives
//!
//! | Type | When to reach for it |
//! |------|----------------------|
//! | [`RingBuf`] | Single-owner ring — simple, no atomics, `&mut` access. |
//! | [`SeqRing`] | Lock-free SPSC ring that **overwrites** old entries (lossy, high-throughput). |
//! | [`EventBuf`] | Lock-free SPSC ring with **backpressure** — rejects pushes when full. |
//!
//! All three are fixed-size, zero-allocation, and generic over `T: Copy`.
//!
//! # Common traits
//!
//! | Trait | Role | Implementors |
//! |-------|------|--------------|
//! | [`Sink<T>`](traits::Sink) | Accept events | `RingBuf`, `seq_ring::Producer`, `event_buf::Producer` |
//! | [`Source<T>`](traits::Source) | Yield events | `seq_ring::Consumer`, `event_buf::Consumer` |
//! | [`Link<In,Out>`](traits::Link) | Both | Blanket impl for any `Sink<In> + Source<Out>` |
//!
//! The [`traits::forward`] function transfers items from any `Source` to any
//! `Sink`, making it easy to bridge different buffer types.
//!
//! # Quick start — `RingBuf`
//! ```
//! use ph_eventing::RingBuf;
//!
//! let mut ring = RingBuf::<u32, 4>::new();
//! ring.push(1);
//! ring.push(2);
//! ring.push(3);
//! assert_eq!(ring.latest(), Some(3));
//! ```
//!
//! # Quick start — `SeqRing`
//! ```
//! use ph_eventing::SeqRing;
//!
//! let ring = SeqRing::<u32, 64>::new();
//! let producer = ring.producer();
//! let mut consumer = ring.consumer();
//!
//! producer.push(42);
//! consumer.poll_one(|seq, v| {
//! assert_eq!(seq, 1);
//! assert_eq!(*v, 42);
//! });
//! ```
//!
//! # Quick start — `EventBuf`
//! ```
//! use ph_eventing::EventBuf;
//!
//! let buf = EventBuf::<u32, 4>::new();
//! let producer = buf.producer();
//! let consumer = buf.consumer();
//!
//! assert!(producer.push(1).is_ok());
//! assert!(producer.push(2).is_ok());
//! assert_eq!(consumer.pop(), Some(1));
//! ```
//!
//! # Quick start — `forward`
//! ```
//! use ph_eventing::{SeqRing, EventBuf};
//! use ph_eventing::traits::{Source, Sink, forward};
//!
//! let seq = SeqRing::<u32, 8>::new();
//! let sp = seq.producer();
//! let mut sc = seq.consumer();
//!
//! sp.push(1); sp.push(2);
//!
//! let eb = EventBuf::<u32, 8>::new();
//! let mut ep = eb.producer();
//!
//! let (n, err) = forward(&mut sc, &mut ep, 10);
//! assert_eq!(n, 2);
//! assert!(err.is_none());
//! ```
//!
//! # No-std
//! The crate is `#![no_std]` by default. Tests require `std`.
//!
//! # Targets without atomics
//! `SeqRing` and `EventBuf` require 32-bit atomics. For targets that lack them
//! (for example `thumbv6m-none-eabi`), enable
//! `portable-atomic-unsafe-assume-single-core` or `portable-atomic-critical-section`.
//! The crate always compiles those modules, so no-atomic targets need one of
//! those features even when only [`RingBuf`] is used. `RingBuf` itself uses no
//! atomics.
//!
//! # Safety and concurrency
//! - `RingBuf` is a plain struct — standard Rust borrow rules apply.
//! - `SeqRing` and `EventBuf` are SPSC by design: exactly one producer and one
//! consumer must be active. `producer()`/`consumer()` will panic if called
//! while another handle of the same kind is active. Using unsafe to bypass
//! these constraints is undefined behavior.
//! - [`EventBuf`] is race-free by construction — its producer and consumer
//! never touch the same slot — and passes Miri with the data-race detector
//! enabled.
//! - [`SeqRing`] is a seqlock and carries a **known formal data race**. The
//! copy is never returned and never becomes an invalid value, but the access
//! is undefined behaviour by the letter of the memory model. Practical
//! consequence: running Miri over a test that drives this ring from two
//! threads reports UB inside this crate — that is the deviation, not a new
//! bug. It is a deliberate trade of formal soundness for accepting any
//! `T: Copy`; the [`seq_ring`] module docs give the alternatives and why each
//! was rejected. [`EventBuf`] has no such caveat, but applies backpressure
//! rather than overwriting, so it is not a drop-in replacement.
//!
//! # Using it across contexts
//! The typical embedded shape is a producer in an interrupt handler and a
//! consumer in a task loop.
//!
//! - [`SeqRing`] and [`EventBuf`] are `Sync` when `T: Send`, so `&buf` can be
//! handed to both contexts. The `Producer` and `Consumer` handles are
//! `Send + !Sync`: move each into the context that owns it, never share one.
//! - The handles borrow the buffer, so the buffer must outlive them.
//! - **`new()` is not a `const fn`**, so
//! `static BUF: EventBuf<u32, 64> = EventBuf::new();` will not compile. Use a
//! `StaticCell`, a `OnceCell`, or a binding in `main` that outlives its
//! borrowers.
//!
//! `N` is fixed at compile time and the buffer lives inline —
//! `N * size_of::<T>()` bytes, no allocation. For [`EventBuf`] it is the
//! backpressure threshold; for [`SeqRing`] it is how far the consumer may lag
//! before entries are lost. It need not be a power of two.
//!
//! # SeqRing semantics
//! - Sequence numbers are monotonically increasing `u32` values; `0` is reserved for "empty".
//! - `poll_one`/`poll_up_to` drain in-order and return `PollStats`.
//! - `latest` reads the newest value without advancing the consumer cursor.
//! - If the consumer lags by more than `N`, it skips ahead and reports drops via `PollStats`.
//! - `Consumer::dropped` saturates rather than wrapping; `usize` is 32 bits on
//! the targets this crate ships to, so a long-lived lagging consumer can
//! reach the top of the range.
//!
//! # EventBuf semantics
//! - `push` returns `Err(val)` when the buffer is full — no data is silently lost.
//! - `pop` returns the oldest item, or `None` when empty.
//! - `drain(max, hook)` consumes up to `max` items through a callback.
compile_error!;
// NOTE: `portable-atomic-unsafe-assume-single-core` and
// `portable-atomic-critical-section` select different portable-atomic backends
// and cannot both be enabled — which makes `--all-features` unsupported for
// this crate. The guard for it lives in build.rs, not here: both features
// forward straight to portable-atomic, whose own `compile_error!` fires while
// the dependency compiles, so a guard in this file would never be reached. A
// build script does not depend on portable-atomic and runs regardless.
pub
pub use EventBuf;
pub use RingBuf;
pub use ;
pub use ;
extern crate std;
/// Helpers shared by the concurrency tests.
pub