indexbus_abi/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unreachable_pub, rust_2018_idioms)]
3#![deny(missing_docs)]
4
5//! IndexBus v1 ABI layouts.
6//!
7//! This crate is the **single source of truth** for the shared-memory ABI:
8//! Rust defines the layouts; C headers are generated from these definitions.
9//!
10//! ## v1 rules
11//!
12//! - All layouts are `#[repr(C)]` and (where required) `#[repr(align(64))]`.
13//! - Layouts are **append-only** within ABI v1. A newer producer may append extra sections,
14//! and older consumers must validate compatibility using [`LayoutHeader`].
15//! - Optional appended wake sections (see [`layouts::wake`]) must start at a **64-byte aligned**
16//! offset.
17//! - All integer fields are **host-endian**.
18//! - The ABI assumes a **64-byte cache line** for padding/alignment decisions.
19//!
20//! ### v1 append-only policy (normative)
21//!
22//! ABI v1 is append-only:
23//!
24//! - Existing exported layout types and fields must not change size, alignment, order, or meaning.
25//! - New capabilities are added by **appending new sections at the end of a region**.
26//! - Append-only additions must be discoverable via:
27//! - [`LayoutHeader::capabilities`] (feature presence)
28//! - [`LayoutHeader::layout_bytes`] (mapped byte length)
29//! - Older readers must ignore trailing bytes they do not understand, after validating the base
30//! layout and required capabilities.
31//!
32//! In practice:
33//! - Concrete exported layouts (e.g., [`layouts::StateLayout256`]) include explicit padding so any
34//! appended sections start at a 64-byte boundary.
35//! - Any optional appended section must be capability-gated and validated at runtime before use.
36//!
37//! ## Validation / initialization
38//!
39//! This crate defines layouts; it does not perform mapping or validation.
40//!
41//! At runtime, consumers should validate at minimum:
42//!
43//! - [`LayoutHeader::is_compatible_v1`] is `true`.
44//! - The mapped region length is at least `LayoutHeader::layout_bytes`.
45//! - `LayoutHeader::capabilities` contains all required capability bits for the intended usage.
46//!
47//! ## Atomics in the ABI
48//!
49//! ABI atomic fields are represented as integer-backed newtypes
50//! ([`IndexbusAtomicU32`]/[`IndexbusAtomicU64`]) so the generated C header stays portable,
51//! while Rust still has an interior-mutability marker.
52//! The contract is:
53//!
54//! - Atomic operations are performed **at the address of the integer field**.
55//! - Size/alignment must match the platform's `AtomicU32`/`AtomicU64`.
56//!
57//! Downstream Rust crates should do atomic operations by casting `&IndexbusAtomicU32`/`&IndexbusAtomicU64`
58//! to `&AtomicU32`/`&AtomicU64` at the same address (see `indexbus-core` internal helpers).
59//!
60//! ## Header generation
61//!
62//! The workspace provides an `xtask` that generates `spec/indexbus_v1.h`.
63//!
64//! ```text
65//! cargo run -p xtask -- gen-headers
66//! ```
67//!
68//! ## ABI sanity checks (doctest)
69//!
70//! ```
71//! use core::mem::{align_of, size_of};
72//! use indexbus_abi::layouts::{StateLayout256, WakeCell};
73//! use indexbus_abi::LayoutHeader;
74//!
75//! assert_eq!(size_of::<LayoutHeader>(), 16);
76//! assert_eq!(size_of::<WakeCell>(), 64);
77//! assert_eq!(align_of::<WakeCell>(), 64);
78//! assert_eq!(align_of::<StateLayout256>(), 64);
79//! // Concrete state layouts must pad so an appended wake section starts 64-aligned.
80//! assert_eq!(size_of::<StateLayout256>() % 64, 0);
81//! ```
82
83#[cfg(feature = "std")]
84extern crate std;
85
86/// Fixed v1 constants that must match the generated C header.
87pub mod constants;
88/// Common layout header and ABI capability bits.
89pub mod header;
90/// `#[repr(C)]` shared-memory layouts.
91pub mod layouts;
92
93pub use constants::*;
94pub use header::{
95 caps, flags, IndexbusAtomicU32, IndexbusAtomicU64, LayoutHeader, INDEXBUS_ABI_VERSION_1,
96 INDEXBUS_MAGIC_IBUS,
97};
98
99#[cfg(test)]
100mod layout_tests;
101
102// ---- Compile-time ABI invariants for exported v1 layouts ----
103
104#[allow(dead_code)]
105const _: () = {
106 use core::mem::{align_of, size_of};
107
108 use crate::layouts::{
109 FanoutWakeSection4, IndexQueue, MpscQueue, SharedFanoutLayout4, SharedLayout,
110 SharedWakeSection, Slot, SlotPoolLayout, StateLayout256, StateWakeSection, WakeCell,
111 };
112
113 // Header + atomics are asserted in `header.rs`; keep key exported layout invariants here.
114 assert!(size_of::<WakeCell>() == 64);
115 assert!(align_of::<WakeCell>() == 64);
116
117 assert!(align_of::<SharedLayout>() == 64);
118 assert!(align_of::<SharedFanoutLayout4>() == 64);
119 assert!(align_of::<StateLayout256>() == 64);
120
121 assert!(align_of::<SlotPoolLayout>() == 64);
122 assert!(align_of::<IndexQueue>() == 64);
123 assert!(align_of::<MpscQueue>() == 64);
124
125 assert!(size_of::<SharedWakeSection>() == 128);
126 assert!(align_of::<SharedWakeSection>() == 64);
127 assert!(size_of::<StateWakeSection>() == 64);
128 assert!(align_of::<StateWakeSection>() == 64);
129 assert!(align_of::<FanoutWakeSection4>() == 64);
130
131 // Concrete state layouts must pad so an appended wake section starts 64-aligned.
132 assert!(size_of::<StateLayout256>().is_multiple_of(64));
133
134 // Slot layout must remain stable (len + data bound by INDEXBUS_SLOT_DATA_SIZE).
135 assert!(size_of::<Slot>() == 4 + 4 + 4 + 4 + crate::INDEXBUS_SLOT_DATA_SIZE);
136};
137
138/// Curated prelude for downstream crates.
139pub mod prelude {
140 pub use crate::caps;
141 pub use crate::layouts::*;
142 pub use crate::{
143 IndexbusAtomicU32, IndexbusAtomicU64, LayoutHeader, INDEXBUS_EMPTY_FREE_U32,
144 INDEXBUS_FANOUT_CONSUMERS_DEFAULT, INDEXBUS_QUEUE_CAPACITY, INDEXBUS_SLOTS_CAPACITY,
145 INDEXBUS_SLOT_DATA_SIZE,
146 };
147}