round_pipers 0.2.0

A way to pipe ndarrays using circular buffers
Documentation
//! # Round Pipers
//!
//! A high-performance Rust library for streaming data processing with circular buffers and read-only pipes. 
//! Designed for simple developers who want to process data without dealing with complex Rust concepts.
//!
//! ## Features
//!
//! - **Circular Buffer Pipes**: High-performance streaming with Linux `memfd` + double-mapped memory
//! - **Read-Only Pipes**: Zero-copy access to pre-populated data using lifetimes
//! - **Write-Only Pipes**: Efficient buffer filling and streaming to external destinations
//! - **Unified API**: All pipe types provide identical reader interfaces
//! - **RAII Memory Management**: Automatic pointer advancement with `ChunkGuard`
//! - **Thread Safety**: Multiple readers, single writer model with `Send + Sync`
//! - **Multi-dimensional Support**: Built on `ndarray` for complex data shapes
//!
//! ## Quick Start
//!
//! ### Circular Buffer Pipes (Streaming Data)
//!
//! ```rust,no_run
//! use round_pipers::Pipe;
//! use std::sync::Arc;
//! use std::path::Path;
//!
//! # fn main() -> round_pipers::Result<()> {
//! // Create a pipe for streaming f64 values
//! let pipe = Arc::new(Pipe::new("my_buffer", 1024, [])?);
//! let writer = pipe.clone().get_writer()?;
//! let reader = pipe.clone().get_reader();
//!
//! // Write data
//! writer.write(100, |mut chunk, _state| {
//!     for i in 0..100 {
//!         chunk[i] = i as f64;
//!     }
//! })?;
//!
//! // Read data with iterator - no lifetime management needed
//! for chunk_result in reader.iter_chunks(25, 25) {
//!     let chunk = chunk_result?;
//!     // Process chunk - automatically advances when dropped
//!     for &value in chunk.iter() {
//!         println!("Value: {}", value);
//!     }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ### Read-Only Pipes (Pre-populated Data)
//!
//! ```rust
//! use round_pipers::ReadOnlyPipe;
//!
//! # fn main() -> round_pipers::Result<()> {
//! // Super simple API - just pass your slice
//! let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
//! let pipe = ReadOnlyPipe::new(&data, [])?;
//! let reader = pipe.get_reader();
//!
//! // Same API as regular pipes - no learning curve
//! for chunk_result in reader.iter_chunks(2, 2) {
//!     let chunk = chunk_result?;
//!     for &value in chunk.iter() {
//!         println!("Value: {}", value);
//!     }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Core Concepts
//!
//! ### Reading and Writing
//!
//! You write ndarrays of (relatively) arbitrary shape into a pipe by calling the `write` function on a pipe's writer, and pass in a closure
//! that gets an array to write to.
//!
//! You read ndarrays from a pipe by calling the `read` function on
//! a pipe's reader, and pass in a closure with the buffer to process, or by iterating over the
//! pipe using `iter_chunks()`. Generally you specify how much data to transfer (`n_to_read`),
//! and how much to advance your read pointer by (`n_to_consume`). 
//!
//! - **Overlapping reads**: `n_to_consume` < `n_to_read` (sliding window)
//! - **Skip data**: `n_to_consume` > `n_to_read`
//! - **Normal processing**: `n_to_consume` = `n_to_read`
//!
//! ## Pipe Types
//!
//! ### 1. Circular Buffer Pipes (for streaming data)
//! A circular shared-memory buffer with support for a single writer and multiple readers.
//! Perfect for audio, signal processing, or streaming pipelines.
//! Uses Linux `memfd` + double-mapped memory for seamless wraparound.
//!
//! ### 2. Read-Only Pipes (for pre-populated data)  
//! Access pre-existing data slices with the same API as circular pipes.
//! Simple lifetime-based approach - no data copying. Perfect for processing existing datasets or arrays.
//!
//! ### 3. Write-Only Pipes (for filling buffers and streaming)
//! Two variants for different use cases:
//!
//! #### WriteOnlyPipeBuffer
//! Write data into pre-allocated mutable slices until full.
//! Simple lifetime-based approach - no data copying, returns errors when full.
//! Perfect for filling output buffers or collecting results.
//!
//! #### WriteOnlyPipeStream  
//! Write typed data to any std::io::Write implementor (files, sockets, etc.).
//! Owns a growable buffer, converts data to native-endian bytes and writes immediately.
//! Perfect for streaming data to files or network destinations with minimal allocations.
//!
//! ## Advanced Usage
//!
//! ### Multi-dimensional Data
//!
//! ```rust,no_run
//! use round_pipers::Pipe;
//! use ndarray::Ix4;
//! use std::sync::Arc;
//! use std::path::Path;
//!
//! # fn main() -> round_pipers::Result<()> {
//! // Create a 4D pipe for complex data structures
//! let pipe = Arc::new(Pipe::<f64, Ix4, ()>::new(
//!     Path::new("multi_dim"),
//!     1000,
//!     [10, 20, 30, 40]  // Shape: 10×20×30×40
//! )?);
//!
//! let reader = pipe.clone().get_reader();
//! for chunk_result in reader.iter_chunks(5, 5) {
//!     let chunk = chunk_result?;
//!     // chunk is ArrayView<f64, Ix5> with shape [5, 10, 20, 30, 40]
//!     println!("Chunk shape: {:?}", chunk.shape());
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ### Peek Semantics (Sliding Window)
//!
//! ```rust,no_run
//! # use round_pipers::*;
//! # use std::sync::Arc;
//! # use std::path::Path;
//! # fn main() -> Result<()> {
//! # let pipe = Arc::new(Pipe::new("test", 1000, [])?);
//! # let reader = pipe.get_reader();
//! // Read 50 elements but only consume 10 (sliding window)
//! for chunk_result in reader.iter_chunks(50, 10) {
//!     let chunk = chunk_result?;
//!     // Next iteration will start 10 elements forward, 
//!     // but read 50 elements (40 overlap)
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Design Philosophy
//!
//! This library is designed for **simple developers** who want to process data without dealing with complex Rust concepts:
//!
//! ### You DON'T Need To Understand:
//! - Complex lifetime annotations
//! - Interior mutability patterns  
//! - Trait object dynamics
//! - Memory layout details
//!
//! ### You DO Need To Know:
//! - Data must outlive read-only pipes (Rust will tell you if you get this wrong)
//! - Use `iter_chunks(n_to_read, n_to_consume)` for processing
//! - Chunks are automatically consumed when dropped
//! - All pipe types work the same way from a user perspective
//!
//! ## Performance
//!
//! - **Zero-copy** data access via ArrayView
//! - **Minimal allocations** (just reader state tracking)
//! - **Read-only pipes** have no synchronization overhead
//! - **Circular buffer pipes** handle writer synchronization automatically
//! - **Lock-free reading** in most cases with Fibonacci backoff for contention
//!
//! ## Error Handling
//!
//! - Clear error messages for common mistakes
//! - "Insufficient data" when trying to read past end
//! - "Reader not registered" for invalid reader usage
//! - All errors implement standard Rust error traits
//!
//! ## Requirements
//!
//! - Linux (uses `memfd_create` and `mmap`)
//! - Rust 1.70+
//!
//! # Example FFT Pipeline
//!
//! Here's a complete example demonstrating a multi-threaded audio processing pipeline.
//! You can run this example with: `cargo run --example audio_fft_pipeline`
//!
//! ```rust
#![doc = include_str!("../examples/audio_fft_pipeline.rs")]
//! ```
mod array_helpers;
mod buffer;
mod error;
mod iterator_common;
mod pipe_common;
mod ro_pipe;
mod rw_pipe;
mod traits;
mod wo_pipe;

// Re-export main types and traits
pub use error::{PipeError, Result};
pub use iterator_common::ChunkGuard;
pub use pipe_common::PipeState;
pub use ro_pipe::{ReadOnlyPipe, ReadOnlyPipeIterator, ReadOnlyPipeReader};
pub use rw_pipe::{Pipe, PipeIterator, PipeReader, PipeWriter};
pub use traits::{ChunkSource, Readable, SizedDimension, Writable};
pub use wo_pipe::{WriteOnlyPipeBuffer, WriteOnlyPipeStream};