Expand description
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> | Accept events | RingBuf, seq_ring::Producer, event_buf::Producer |
Source<T> | Yield events | seq_ring::Consumer, event_buf::Consumer |
Link<In,Out> | 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.
RingBuf has no atomics requirement.
§Safety and concurrency
RingBufis a plain struct — standard Rust borrow rules apply.SeqRingandEventBufare 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.
§SeqRing semantics
- Sequence numbers are monotonically increasing
u32values;0is reserved for “empty”. poll_one/poll_up_todrain in-order and returnPollStats.latestreads the newest value without advancing the consumer cursor.- If the consumer lags by more than
N, it skips ahead and reports drops viaPollStats.
§EventBuf semantics
pushreturnsErr(val)when the buffer is full — no data is silently lost.popreturns the oldest item, orNonewhen empty.drain(max, hook)consumes up tomaxitems through a callback.
Re-exports§
pub use event_buf::EventBuf;pub use ring::RingBuf;pub use seq_ring::PollStats;pub use seq_ring::SeqRing;pub use traits::Link;pub use traits::Sink;pub use traits::Source;