Skip to main content

ph_eventing/
lib.rs

1//! Stack-allocated ring buffers for no-std embedded targets.
2//!
3//! # Primitives
4//!
5//! | Type | When to reach for it |
6//! |------|----------------------|
7//! | [`RingBuf`] | Single-owner ring — simple, no atomics, `&mut` access. |
8//! | [`SeqRing`] | Lock-free SPSC ring that **overwrites** old entries (lossy, high-throughput). |
9//! | [`EventBuf`] | Lock-free SPSC ring with **backpressure** — rejects pushes when full. |
10//!
11//! All three are fixed-size, zero-allocation, and generic over `T: Copy`.
12//!
13//! # Common traits
14//!
15//! | Trait | Role | Implementors |
16//! |-------|------|--------------|
17//! | [`Sink<T>`](traits::Sink) | Accept events | `RingBuf`, `seq_ring::Producer`, `event_buf::Producer` |
18//! | [`Source<T>`](traits::Source) | Yield events | `seq_ring::Consumer`, `event_buf::Consumer` |
19//! | [`Link<In,Out>`](traits::Link) | Both | Blanket impl for any `Sink<In> + Source<Out>` |
20//!
21//! The [`traits::forward`] function transfers items from any `Source` to any
22//! `Sink`, making it easy to bridge different buffer types.
23//!
24//! # Quick start — `RingBuf`
25//! ```
26//! use ph_eventing::RingBuf;
27//!
28//! let mut ring = RingBuf::<u32, 4>::new();
29//! ring.push(1);
30//! ring.push(2);
31//! ring.push(3);
32//! assert_eq!(ring.latest(), Some(3));
33//! ```
34//!
35//! # Quick start — `SeqRing`
36//! ```
37//! use ph_eventing::SeqRing;
38//!
39//! let ring = SeqRing::<u32, 64>::new();
40//! let producer = ring.producer();
41//! let mut consumer = ring.consumer();
42//!
43//! producer.push(42);
44//! consumer.poll_one(|seq, v| {
45//!     assert_eq!(seq, 1);
46//!     assert_eq!(*v, 42);
47//! });
48//! ```
49//!
50//! # Quick start — `EventBuf`
51//! ```
52//! use ph_eventing::EventBuf;
53//!
54//! let buf = EventBuf::<u32, 4>::new();
55//! let producer = buf.producer();
56//! let consumer = buf.consumer();
57//!
58//! assert!(producer.push(1).is_ok());
59//! assert!(producer.push(2).is_ok());
60//! assert_eq!(consumer.pop(), Some(1));
61//! ```
62//!
63//! # Quick start — `forward`
64//! ```
65//! use ph_eventing::{SeqRing, EventBuf};
66//! use ph_eventing::traits::{Source, Sink, forward};
67//!
68//! let seq = SeqRing::<u32, 8>::new();
69//! let sp = seq.producer();
70//! let mut sc = seq.consumer();
71//!
72//! sp.push(1); sp.push(2);
73//!
74//! let eb = EventBuf::<u32, 8>::new();
75//! let mut ep = eb.producer();
76//!
77//! let (n, err) = forward(&mut sc, &mut ep, 10);
78//! assert_eq!(n, 2);
79//! assert!(err.is_none());
80//! ```
81//!
82//! # No-std
83//! The crate is `#![no_std]` by default. Tests require `std`.
84//!
85//! # Targets without atomics
86//! `SeqRing` and `EventBuf` require 32-bit atomics. For targets that lack them
87//! (for example `thumbv6m-none-eabi`), enable
88//! `portable-atomic-unsafe-assume-single-core` or `portable-atomic-critical-section`.
89//! `RingBuf` has no atomics requirement.
90//!
91//! # Safety and concurrency
92//! - `RingBuf` is a plain struct — standard Rust borrow rules apply.
93//! - `SeqRing` and `EventBuf` are SPSC by design: exactly one producer and one
94//!   consumer must be active. `producer()`/`consumer()` will panic if called
95//!   while another handle of the same kind is active. Using unsafe to bypass
96//!   these constraints is undefined behavior.
97//!
98//! # SeqRing semantics
99//! - Sequence numbers are monotonically increasing `u32` values; `0` is reserved for "empty".
100//! - `poll_one`/`poll_up_to` drain in-order and return `PollStats`.
101//! - `latest` reads the newest value without advancing the consumer cursor.
102//! - If the consumer lags by more than `N`, it skips ahead and reports drops via `PollStats`.
103//!
104//! # EventBuf semantics
105//! - `push` returns `Err(val)` when the buffer is full — no data is silently lost.
106//! - `pop` returns the oldest item, or `None` when empty.
107//! - `drain(max, hook)` consumes up to `max` items through a callback.
108#![no_std]
109
110#[cfg(all(not(target_has_atomic = "32"), not(feature = "portable-atomic")))]
111compile_error!(
112    "ph-eventing requires 32-bit atomics. For thumbv6m and other no-atomic targets, \
113enable either the portable-atomic-unsafe-assume-single-core or portable-atomic-critical-section feature."
114);
115
116pub mod event_buf;
117pub mod ring;
118pub mod seq_ring;
119pub mod traits;
120
121pub use event_buf::EventBuf;
122pub use ring::RingBuf;
123pub use seq_ring::{PollStats, SeqRing};
124pub use traits::{Link, Sink, Source};
125
126#[cfg(test)]
127extern crate std;