vmcircbuffer 0.0.13

Double Mapped Circular Buffer
Documentation
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Build
cargo build

# Run all tests
cargo test

# Run tests for a specific module
cargo test --test lockfree
cargo test --test sync
cargo test --test async
cargo test --test nonblocking

# Run a single test by name
cargo test --test lockfree fuzz_lockfree

# Lint
cargo clippy

# Run examples
cargo run --example sdr --features sync
```

## Architecture

This is a **double-mapped circular buffer** library. The core trick: the underlying memory is mapped twice, back-to-back in virtual address space, so data always appears as a contiguous slice — no wraparound handling needed by the caller.

### Layer structure (bottom to top)

1. **`double_mapped_buffer/`** — Platform-specific OS memory mapping.
   - `mod.rs`: Exposes `DoubleMappedBuffer<T>` and `pagesize()`. Selects `unix.rs` or `windows.rs` at compile time.
   - `double_mapped_buffer.rs`: Generic wrapper around platform impl. Key methods: `slice_with_offset()` / `slice_with_offset_mut()` return a `capacity`-length slice starting at an arbitrary offset into the circular region.
   - Buffer size is always the LCM of page size and `size_of::<T>()`.

2. **`generic.rs`** — The main circular buffer state machine, parameterized over a `Notifier` type.
   - `Circular::with_capacity::<T, N, M>()` creates a `Writer<T, N, M>`.
   - State is shared via `Arc<Mutex<State<N, M>>>` (uses `spin::Mutex`).
   - Each reader tracks its own offset and A/B phase bit to detect full vs. empty.
   - `Writer::slice(arm)` / `Reader::slice(arm)`: the `arm` flag arms the notifier so it will fire when space/data becomes available.
   - Dropping `Writer` sets `writer_done = true` and notifies all readers (signals EOF).
   - Dropping a `Reader` removes it from the slab and notifies the writer.

3. **`Notifier` trait** (`lib.rs`) — Two methods: `arm()` and `notify()`. Implementations:
   - `sync`: `std::sync::mpsc` channel — `notify()` sends `()`, `slice()` calls `recv()` in a loop.
   - `asynchronous`: `futures::channel::mpsc` channel — `slice()` is `async` and `.await`s.
   - `nonblocking`: `NullNotifier``arm`/`notify` are no-ops; callers must poll manually.

4. **`lockfree.rs`** — Independent SPMC implementation (does not use `generic.rs`).
   - Uses `AtomicUsize` for writer and reader positions; no global mutex.
   - Reader slots are pre-allocated (`Vec<ReaderSlot>` sized at construction via `max_readers`).
   - Reader registration uses CAS on `ReaderSlot::state` (`INACTIVE → INITIALIZING → ACTIVE`).
   - Positions are monotonically increasing (wrapping); space = `cap - max_dist` across active readers.
   - Per-reader metadata uses `spin::Mutex<M>` only; main data path is lock-free.
   - `lockfree::Writer::slice()` / `Reader::slice()` return immediately (non-blocking); callers spin if needed.

### Metadata / Tags

Both `generic` and `lockfree` support a `Metadata` trait for attaching typed metadata (tags) to buffer items. `NoMetadata` is a zero-cost no-op impl. The trait tracks offsets as items are produced and consumed.

### Features

All four implementations are gated by Cargo features (`async`, `sync`, `nonblocking`, `lockfree`), all enabled by default. The `generic` feature enables the underlying generic layer; `async`, `sync`, `nonblocking`, and `lockfree` all depend on it.