rapace_transport_shm/lib.rs
1//! rapace-transport-shm: Shared memory transport for rapace.
2//!
3//! This is the **performance reference** implementation. It defines the
4//! canonical memory layout and zero-copy patterns.
5//!
6//! # Characteristics
7//!
8//! - SPSC rings for descriptors
9//! - Slab allocator for payloads
10//! - Zero-copy when data is already in SHM
11//! - eventfd doorbells for async notification (future)
12//! - Generation counters for crash safety
13//!
14//! # Architecture
15//!
16//! Each SHM segment represents a session between exactly two peers (A and B).
17//! The design is intentionally SPSC (single-producer, single-consumer) per ring.
18//!
19//! ```text
20//! ┌─────────────────────────────────────────────────────────────────────┐
21//! │ Segment Header (64 bytes) │
22//! ├─────────────────────────────────────────────────────────────────────┤
23//! │ A→B Descriptor Ring │
24//! ├─────────────────────────────────────────────────────────────────────┤
25//! │ B→A Descriptor Ring │
26//! ├─────────────────────────────────────────────────────────────────────┤
27//! │ Data Segment (slab allocator) │
28//! └─────────────────────────────────────────────────────────────────────┘
29//! ```
30//!
31//! # Optional: SHM Allocator
32//!
33//! Enable the `allocator` feature to get [`ShmAllocator`], which allows allocating
34//! data directly into SHM slots. When such data is passed through the encoder,
35//! it's detected as already in SHM and referenced zero-copy (no memcpy).
36//!
37//! ```toml
38//! [dependencies]
39//! rapace-transport-shm = { version = "0.1", features = ["allocator"] }
40//! ```
41//!
42//! **Important:** This is an optional optimization. Service traits remain
43//! transport-agnostic—they don't know or care whether data is in SHM.
44//! The allocator is for callers who want to pre-allocate in SHM for performance.
45
46#[cfg(any(feature = "allocator", test))]
47mod alloc;
48pub mod doorbell;
49pub mod futex;
50pub mod hub_alloc;
51pub mod hub_layout;
52pub mod hub_session;
53pub mod hub_transport;
54pub mod layout;
55mod session;
56mod transport;
57
58#[cfg(any(feature = "allocator", test))]
59pub use alloc::ShmAllocator;
60pub use layout::{
61 DEFAULT_RING_CAPACITY, DEFAULT_SLOT_COUNT, DEFAULT_SLOT_SIZE, DataSegment, DataSegmentHeader,
62 DescRing, DescRingHeader, LayoutError, RingError, RingStatus, SegmentHeader, SegmentOffsets,
63 SlotError, SlotMeta, SlotState, calculate_segment_size,
64};
65pub use session::{ShmSession, ShmSessionConfig};
66pub use transport::{ShmMetrics, ShmTransport};
67
68// Hub architecture re-exports
69pub use doorbell::{Doorbell, close_peer_fd};
70pub use hub_alloc::{HubAllocator, HubSlotStatus, SizeClassStatus};
71pub use hub_layout::{
72 ExtentHeader, HUB_SIZE_CLASSES, HubHeader, HubOffsets, HubSlotError, HubSlotMeta, PeerEntry,
73 SizeClassHeader, decode_slot_ref, encode_slot_ref,
74};
75pub use hub_session::{HubConfig, HubHost, HubPeer, PeerInfo};
76pub use hub_transport::{
77 HostPeerHandle, HubHostPeerTransport, HubHostTransport, HubPeerTransport, HubTransportError,
78 INLINE_PAYLOAD_SIZE, INLINE_PAYLOAD_SLOT,
79};
80
81// Re-export allocator-api2 types for convenience when the feature is enabled.
82#[cfg(feature = "allocator")]
83pub use allocator_api2;
84
85// ============================================================================
86// Helper functions for easy SHM allocation
87// ============================================================================
88
89/// Create a `Vec<u8>` allocated in SHM from a byte slice.
90///
91/// This is a convenience function for the common pattern of allocating
92/// a buffer in SHM and copying data into it. When the resulting Vec is
93/// passed through the encoder, it will be detected as already in SHM
94/// and referenced zero-copy.
95///
96/// # Example
97///
98/// ```ignore
99/// use rapace_transport_shm::{ShmSession, ShmAllocator, shm_vec};
100///
101/// let (session, _) = ShmSession::create_pair().unwrap();
102/// let alloc = ShmAllocator::new(session.clone());
103///
104/// // Allocate PNG data in SHM
105/// let png_bytes = std::fs::read("image.png").unwrap();
106/// let shm_png = shm_vec(&alloc, &png_bytes);
107///
108/// // When passed to the encoder, this is zero-copy!
109/// ```
110#[cfg(any(feature = "allocator", test))]
111pub fn shm_vec(alloc: &ShmAllocator, bytes: &[u8]) -> allocator_api2::vec::Vec<u8, ShmAllocator> {
112 let mut vec = allocator_api2::vec::Vec::new_in(alloc.clone());
113 vec.extend_from_slice(bytes);
114 vec
115}
116
117/// Create an empty `Vec<u8>` allocated in SHM with the given capacity.
118///
119/// Use this when you need to build up data incrementally but want it
120/// to end up in SHM for zero-copy transmission.
121///
122/// # Example
123///
124/// ```ignore
125/// use rapace_transport_shm::{ShmSession, ShmAllocator, shm_vec_with_capacity};
126///
127/// let (session, _) = ShmSession::create_pair().unwrap();
128/// let alloc = ShmAllocator::new(session.clone());
129///
130/// // Pre-allocate buffer in SHM
131/// let mut buf = shm_vec_with_capacity(&alloc, 4096);
132/// buf.extend_from_slice(b"header: ");
133/// buf.extend_from_slice(b"value\n");
134///
135/// // When passed to the encoder, this is zero-copy!
136/// ```
137#[cfg(any(feature = "allocator", test))]
138pub fn shm_vec_with_capacity(
139 alloc: &ShmAllocator,
140 capacity: usize,
141) -> allocator_api2::vec::Vec<u8, ShmAllocator> {
142 allocator_api2::vec::Vec::with_capacity_in(capacity, alloc.clone())
143}