Crate fring

source ·
Expand description

Fast ring buffer intended for no_std targets.

fring (“fast ring”) is a fast and lightweight circular buffer, designed for embedded systems and other no_std targets. The memory footprint is the buffer itself plus two usize indices, and that’s it. The buffer allows a single producer and a single consumer, which may operate concurrently. Memory safety and thread safety are enforced at compile time; the buffer is lock-free at runtime. The buffer length is required to be a power of two, and the only arithmetic operations used by buffer operations are addition/subtraction and bitwise and.

Example of threaded use:

fn main() {
    let mut buffer = fring::Buffer::<N>::new();
    let (producer, consumer) = buffer.split();
    std::thread::scope(|s| {
        s.spawn(|| {
            make_data(producer);
        });
        use_data(consumer);
    });
}

Example of static (no_std) use:

static BUFFER: fring::Buffer<N> = fring::Buffer::new();

fn interrupt_handler() {
    // UNSAFE: this is safe because this is the only place we ever
    // call BUFFER.producer(), and interrupt_handler() is not reentrant
    let producer = unsafe { BUFFER.producer() };
    write_data(producer);
}

fn main() {
    // UNSAFE: this is safe because this is the only place we ever
    // call BUFFER.consumer(), and main() is not reentrant
    let consumer = unsafe { BUFFER.consumer() };
    use_data(consumer);
}

Structs

  • A Buffer<N> consists of a [u8; N] array along with two usize indices into the array. N must be a power of two. (If you need more flexibility with sizing, consider using a bbqueue::BBBuffer instead.) A Buffer<N> can hold N bytes of data and guarantees FIFO ordering. The only way to use a Buffer is to split it into a Producer and a Consumer, which may then be passed to different threads or contexts.
  • A Consumer is a smart pointer to a Buffer, which is endowed with the right to remove data from the buffer. Only one Consumer may exist at one time for any given buffer. Requesting a ReadRegion from a Consumer is the only way to read data out of a Buffer.
  • A Producer is a smart pointer to a Buffer, which is endowed with the right to add data into the buffer. Only one Producer may exist at one time for any given buffer. Requesting a WriteRegion from a Producer is the only way to insert data into a Buffer.
  • A ReadRegion is a smart pointer to a specific region of data in a Buffer. The ReadRegion derefs to [u8] and may generally be used in the same way as a slice (e.g. r[i], r.len()). When a ReadRegion is dropped, it updates the associated Buffer to indicate that the its memory region may now be overwritten. If a ReadRegion is forgotten instead of dropped, the buffer will not be updated and its memory will be read again by the next read from the buffer.
  • A WriteRegion is a smart pointer to a specific region of data in a Buffer. The WriteRegion derefs to [u8] and may generally be used in the same way as a slice (e.g. w[i], w.len()). When a WriteRegion is dropped, it updates the associated Buffer to indicate that its memory region now contains data which is ready to be read. If a WriteRegion is forgotten instead of dropped, the buffer will not be updated and its memory will be overwritten by the next write to the buffer.