shm_primitives/
segment.rs1use crate::sync::{AtomicU32, AtomicU64, Ordering};
2
3pub const MAGIC: [u8; 8] = *b"ROAMHUB\x07";
7
8pub const SEGMENT_VERSION: u32 = 7;
10
11pub const SEGMENT_HEADER_SIZE: usize = 128;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct SegmentHeaderInit {
17 pub total_size: u64,
18 pub max_payload_size: u32,
19 pub inline_threshold: u32,
20 pub max_guests: u32,
21 pub bipbuf_capacity: u32,
22 pub peer_table_offset: u64,
23 pub var_pool_offset: u64,
24 pub heartbeat_interval: u64,
25 pub num_var_slot_classes: u32,
26}
27
28#[repr(C)]
38pub struct SegmentHeader {
39 pub magic: [u8; 8],
41 pub version: u32,
43 pub header_size: u32,
45 pub total_size: u64,
47 pub max_payload_size: u32,
49 pub inline_threshold: u32,
52 pub max_guests: u32,
54 pub bipbuf_capacity: u32,
56 pub peer_table_offset: u64,
58 pub var_pool_offset: u64,
60 pub heartbeat_interval: u64,
62 pub host_goodbye: AtomicU32,
64 pub num_var_slot_classes: u32,
66 pub current_size: AtomicU64,
68 _reserved: [u8; 48],
69}
70
71#[cfg(not(loom))]
72const _: () = assert!(core::mem::size_of::<SegmentHeader>() == SEGMENT_HEADER_SIZE);
73
74impl SegmentHeader {
75 pub unsafe fn init(&mut self, init: SegmentHeaderInit) {
81 self.magic = MAGIC;
82 self.version = SEGMENT_VERSION;
83 self.header_size = SEGMENT_HEADER_SIZE as u32;
84 self.total_size = init.total_size;
85 self.max_payload_size = init.max_payload_size;
86 self.inline_threshold = init.inline_threshold;
87 self.max_guests = init.max_guests;
88 self.bipbuf_capacity = init.bipbuf_capacity;
89 self.peer_table_offset = init.peer_table_offset;
90 self.var_pool_offset = init.var_pool_offset;
91 self.heartbeat_interval = init.heartbeat_interval;
92 self.host_goodbye = AtomicU32::new(0);
93 self.num_var_slot_classes = init.num_var_slot_classes;
94 self.current_size = AtomicU64::new(init.total_size);
95 self._reserved = [0u8; 48];
96 }
97
98 pub fn validate(&self) -> Result<(), &'static str> {
104 if self.magic != MAGIC {
105 return Err("bad magic: not a roam v7 segment");
106 }
107 if self.version != SEGMENT_VERSION {
108 return Err("unsupported segment version");
109 }
110 if self.header_size != SEGMENT_HEADER_SIZE as u32 {
111 return Err("unexpected header_size");
112 }
113 if self.num_var_slot_classes == 0 {
114 return Err("segment missing var-slot classes");
115 }
116 Ok(())
117 }
118
119 #[inline]
121 pub fn effective_inline_threshold(&self) -> u32 {
122 if self.inline_threshold == 0 {
123 256
124 } else {
125 self.inline_threshold
126 }
127 }
128
129 #[inline]
131 pub fn current_size(&self) -> u64 {
132 self.current_size.load(Ordering::Acquire)
133 }
134
135 #[inline]
137 pub fn host_goodbye(&self) -> bool {
138 self.host_goodbye.load(Ordering::Acquire) != 0
139 }
140}
141
142#[cfg(all(test, not(loom)))]
143mod tests {
144 use super::*;
145 use crate::region::HeapRegion;
146
147 fn make_header() -> (HeapRegion, *mut SegmentHeader) {
148 let region = HeapRegion::new_zeroed(SEGMENT_HEADER_SIZE);
149 let r = region.region();
150 let hdr: *mut SegmentHeader = unsafe { r.get_mut::<SegmentHeader>(0) };
151 unsafe {
152 (*hdr).init(SegmentHeaderInit {
153 total_size: 65536,
154 max_payload_size: 65536,
155 inline_threshold: 0,
156 max_guests: 4,
157 bipbuf_capacity: 16384,
158 peer_table_offset: 128,
159 var_pool_offset: 4096,
160 heartbeat_interval: 0,
161 num_var_slot_classes: 1,
162 });
163 }
164 (region, hdr)
165 }
166
167 #[test]
168 fn roundtrip() {
169 let (_region, hdr) = make_header();
170 let hdr = unsafe { &*hdr };
171
172 assert_eq!(hdr.magic, MAGIC);
173 assert_eq!(hdr.version, SEGMENT_VERSION);
174 assert_eq!(hdr.header_size, 128);
175 assert_eq!(hdr.total_size, 65536);
176 assert_eq!(hdr.max_guests, 4);
177 assert_eq!(hdr.bipbuf_capacity, 16384);
178 assert_eq!(hdr.peer_table_offset, 128);
179 assert_eq!(hdr.var_pool_offset, 4096);
180 assert_eq!(hdr.num_var_slot_classes, 1);
181 assert_eq!(hdr.current_size(), 65536);
182 }
183
184 #[test]
185 fn validate_ok() {
186 let (_region, hdr) = make_header();
187 unsafe { &*hdr }.validate().expect("valid header");
188 }
189
190 #[test]
191 fn validate_bad_magic() {
192 let (_region, hdr) = make_header();
193 let hdr = unsafe { &mut *hdr };
194 hdr.magic[7] = 0x01; assert!(hdr.validate().is_err());
196 }
197
198 #[test]
199 fn validate_bad_version() {
200 let (_region, hdr) = make_header();
201 let hdr = unsafe { &mut *hdr };
202 hdr.version = 99;
203 assert!(hdr.validate().is_err());
204 }
205
206 #[test]
207 fn inline_threshold_default() {
208 let (_region, hdr) = make_header();
209 let hdr = unsafe { &*hdr };
210 assert_eq!(hdr.effective_inline_threshold(), 256);
212 }
213
214 #[test]
215 fn host_goodbye_flag() {
216 let (_region, hdr) = make_header();
217 let hdr = unsafe { &*hdr };
218 assert!(!hdr.host_goodbye());
219 hdr.host_goodbye.store(1, Ordering::Release);
220 assert!(hdr.host_goodbye());
221 }
222
223 #[test]
224 fn current_size_matches_total() {
225 let (_region, hdr) = make_header();
226 let hdr = unsafe { &*hdr };
227 assert_eq!(hdr.current_size(), hdr.total_size);
228 }
229}