indexbus_abi/
header.rs

1use core::cell::UnsafeCell;
2
3/// Shared-memory layout header for ABI/version checks.
4///
5/// Keep this at the start of any region you expect to map across processes (and potentially
6/// across versions).
7#[repr(C)]
8#[derive(Copy, Clone)]
9pub struct LayoutHeader {
10    /// Magic value identifying an IndexBus-mapped region.
11    pub magic: u32,
12    /// ABI version number.
13    pub version: u16,
14    /// Layout flags (reserved for future use).
15    pub flags: u16,
16    /// Capability bitset indicating which optional features/sections are present.
17    pub capabilities: u32,
18    /// v1: total size in bytes of the shared layout that starts with this header.
19    ///
20    /// Initializers should set this to the full byte length of the mapped layout they created.
21    /// Consumers should validate that their mapping is at least this length before accessing
22    /// appended sections.
23    pub layout_bytes: u32,
24}
25
26/// Magic used to identify IndexBus-mapped regions.
27///
28/// Encodes the ASCII bytes `IBUS`.
29pub const INDEXBUS_MAGIC_IBUS: u32 = 0x5355_4249; // 'IBUS'
30
31/// ABI version number for IndexBus v1.
32pub const INDEXBUS_ABI_VERSION_1: u16 = 1;
33
34impl LayoutHeader {
35    const MAGIC_INDEXBUS: u32 = INDEXBUS_MAGIC_IBUS;
36    const VERSION_1: u16 = INDEXBUS_ABI_VERSION_1;
37
38    #[inline]
39    /// Construct a v1 header with `layout_bytes` left as 0.
40    ///
41    /// Initializers should fill `layout_bytes` after they know the full mapping length.
42    pub const fn new_v1(capabilities: u32, flags: u16) -> Self {
43        Self {
44            magic: Self::MAGIC_INDEXBUS,
45            version: Self::VERSION_1,
46            flags,
47            capabilities,
48            layout_bytes: 0,
49        }
50    }
51
52    #[inline]
53    /// Construct a v1 header with an explicit `layout_bytes` value.
54    pub const fn new_v1_with_layout_bytes(
55        capabilities: u32,
56        flags: u16,
57        layout_bytes: u32,
58    ) -> Self {
59        Self {
60            magic: Self::MAGIC_INDEXBUS,
61            version: Self::VERSION_1,
62            flags,
63            capabilities,
64            layout_bytes,
65        }
66    }
67
68    #[inline]
69    /// Returns `true` if this header matches the v1 magic and version.
70    ///
71    /// This only checks the ABI identifier and version. Callers should separately validate
72    /// `layout_bytes` and required `capabilities`.
73    pub const fn is_compatible_v1(&self) -> bool {
74        self.magic == Self::MAGIC_INDEXBUS && self.version == Self::VERSION_1
75    }
76}
77
78/// Capability bits stored in `LayoutHeader::capabilities`.
79pub mod caps {
80    /// Region supports the shared events queues (SPSC + MPSC).
81    pub const INDEXBUS_CAP_SUPPORTS_EVENTS: u32 = 1u32 << 0;
82    /// Region supports the state stream layout.
83    pub const INDEXBUS_CAP_SUPPORTS_STATE: u32 = 1u32 << 1;
84    /// Region supports fanout queues.
85    pub const INDEXBUS_CAP_SUPPORTS_FANOUT: u32 = 1u32 << 2;
86    /// Region has an appended wake section suitable for OS blocking.
87    pub const INDEXBUS_CAP_SUPPORTS_BLOCKING: u32 = 1u32 << 3;
88
89    /// Sequencer region: sequencer + gating sequences (new region type).
90    pub const INDEXBUS_CAP_SUPPORTS_SEQUENCER: u32 = 1u32 << 4;
91
92    /// Journal region: replayable append-only journal (new region type).
93    pub const INDEXBUS_CAP_SUPPORTS_JOURNAL: u32 = 1u32 << 5;
94
95    /// Optional appended stats/counters section for journal regions.
96    pub const INDEXBUS_CAP_SUPPORTS_JOURNAL_STATS: u32 = 1u32 << 6;
97}
98
99/// Layout flag bits stored in `LayoutHeader::flags`.
100///
101/// v1 guidance:
102/// - `flags` are not capability bits; they should be used for **disambiguation** and metadata.
103/// - The low 8 bits are reserved for a **region kind** discriminator so tools can classify a
104///   mapping without guessing from capability combinations.
105pub mod flags {
106    /// Mask for the low 8 bits containing the region kind discriminator.
107    pub const INDEXBUS_FLAGS_REGION_KIND_MASK: u16 = 0x00ff;
108
109    /// `LayoutHeader.flags` kind value: shared events region (`layouts::SharedLayout`).
110    pub const INDEXBUS_REGION_KIND_EVENTS: u16 = 1;
111    /// `LayoutHeader.flags` kind value: fanout events region (`layouts::SharedFanoutLayout4`).
112    pub const INDEXBUS_REGION_KIND_FANOUT: u16 = 2;
113    /// `LayoutHeader.flags` kind value: state region (`layouts::StateLayout256`).
114    pub const INDEXBUS_REGION_KIND_STATE: u16 = 3;
115    /// `LayoutHeader.flags` kind value: sequencer region (`layouts::SequencerLayout4`).
116    pub const INDEXBUS_REGION_KIND_SEQUENCER: u16 = 4;
117    /// `LayoutHeader.flags` kind value: journal region (`layouts::JournalLayout4`).
118    pub const INDEXBUS_REGION_KIND_JOURNAL: u16 = 5;
119
120    #[inline]
121    /// Extract the region kind discriminator from `LayoutHeader.flags`.
122    pub const fn region_kind(flags: u16) -> u16 {
123        flags & INDEXBUS_FLAGS_REGION_KIND_MASK
124    }
125}
126
127/// ABI atomics are represented as integer-backed newtypes in layout structs.
128///
129/// Why this exists:
130/// - The ABI needs C-friendly integer storage.
131/// - Rust needs an interior-mutability marker so atomic ops don't violate aliasing rules.
132///
133/// Contract:
134/// - Atomic operations are performed at the address of the inner integer.
135/// - Size/alignment must match `AtomicU32`/`AtomicU64` on the platform.
136///
137/// When the containing layout is accessed concurrently, callers should treat these fields as
138/// atomic storage and avoid non-atomic reads/writes to the inner integer.
139///
140#[repr(transparent)]
141/// Integer-backed ABI atomic `u32`.
142pub struct IndexbusAtomicU32(UnsafeCell<u32>);
143
144#[repr(transparent)]
145/// Integer-backed ABI atomic `u64`.
146pub struct IndexbusAtomicU64(UnsafeCell<u64>);
147
148impl IndexbusAtomicU32 {
149    #[inline]
150    /// Construct an ABI atomic with an initial value.
151    pub const fn new(value: u32) -> Self {
152        Self(UnsafeCell::new(value))
153    }
154}
155
156impl IndexbusAtomicU64 {
157    #[inline]
158    /// Construct an ABI atomic with an initial value.
159    pub const fn new(value: u64) -> Self {
160        Self(UnsafeCell::new(value))
161    }
162}
163
164// These types are used in shared-memory layouts and are accessed via atomics.
165// They must be Send+Sync to allow the *containing* layout to be shared across threads.
166unsafe impl Send for IndexbusAtomicU32 {}
167unsafe impl Sync for IndexbusAtomicU32 {}
168unsafe impl Send for IndexbusAtomicU64 {}
169unsafe impl Sync for IndexbusAtomicU64 {}
170
171// ABI contract checks for integer-backed atomics.
172//
173// If these fail, the layout cannot be safely accessed using Rust atomics at the same address.
174#[allow(dead_code)]
175const _: () = {
176    use core::mem::{align_of, size_of};
177    use core::sync::atomic::{AtomicU32, AtomicU64};
178
179    assert!(size_of::<IndexbusAtomicU32>() == size_of::<AtomicU32>());
180    assert!(align_of::<IndexbusAtomicU32>() == align_of::<AtomicU32>());
181
182    assert!(size_of::<IndexbusAtomicU64>() == size_of::<AtomicU64>());
183    assert!(align_of::<IndexbusAtomicU64>() == align_of::<AtomicU64>());
184};
185
186// Compile-time ABI sanity checks.
187#[allow(dead_code)]
188const _LAYOUT_HEADER_SIZE_CHECK: [u8; 16] = [0u8; core::mem::size_of::<LayoutHeader>()];