Skip to main content

Crate fring

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. (“Circular buffer” means it is a FIFO queue, stored as an array, and the data wraps back to the beginning of the array once it reaches the end.) The memory footprint of a fring::Buffer is the buffer itself plus two usize indices.

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.

The only way to use a Buffer is to split it into a Producer and a Consumer. Then one may call Producer.write() and Consumer.read(), or various other methods which are provided by Producer and Consumer.

For targets which do not provide Rust native atomic types, the optional portable-atomic feature may be enabled to use the portable-atomic crate.

Example of safe threaded use:

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

Example of static use (requires unsafe):

static BUFFER: fring::Buffer<u8, 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§

Buffer
A Buffer<N> consists of a [T; 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 elements of type T 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.
Consumer
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. The methods of a Consumer are the only way to read data out of a Buffer.
Producer
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. The methods of a Producer are the only way to insert data into a Buffer.
Region
A Region is a smart pointer to a specific region of data in a Buffer. The Region derefs to [T] and may generally be used in the same way as a slice (e.g. region[i], region.len()). When a Region is dropped, it updates the associated Buffer to indicate that this section of the buffer is finished being read or written. If a Region is forgotten instead of dropped, the buffer will not be updated and the same region will be re-issued by the next read/write.