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>()];