# 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.