1use crate::opcode::OPCODE_SIZE;
7
8pub const MAGIC: u32 = 0x504C_504C;
10pub const PROTOCOL_VERSION: u16 = 1;
12
13pub const DEFAULT_SLOT_COUNT: usize = 4096;
15pub const DEFAULT_ARENA_BYTES: usize = 1 << 20; pub const DEFAULT_EVENT_ARENA_BYTES: usize = 1 << 20; pub const OFF_MAGIC: usize = 0x00;
23pub const OFF_VERSION: usize = 0x04;
25pub const OFF_RING_OFFSET: usize = 0x06;
27pub const OFF_RING_BYTES: usize = 0x0A;
29pub const OFF_SLOT_COUNT: usize = 0x0E;
31pub const OFF_ARENA_OFFSET: usize = 0x12;
33pub const OFF_ARENA_BYTES: usize = 0x16;
35pub const OFF_ARENA_CURSOR: usize = 0x1A;
37pub const OFF_READ_CURSOR: usize = 0x1E;
39pub const OFF_WRITE_CURSOR: usize = 0x22;
41pub const OFF_FRAME_COUNT: usize = 0x26;
43pub const OFF_EVENT_RING_OFFSET: usize = 0x2A;
45pub const OFF_EVENT_RING_BYTES: usize = 0x2E;
47pub const OFF_EVENT_SLOT_COUNT: usize = 0x32;
49pub const OFF_EVENT_READ_CURSOR: usize = 0x36;
51pub const OFF_EVENT_WRITE_CURSOR: usize = 0x3A;
53pub const OFF_EVENT_ARENA_OFFSET: usize = 0x40;
55pub const OFF_EVENT_ARENA_BYTES: usize = 0x44;
57pub const OFF_EVENT_ARENA_CURSOR: usize = 0x48;
59pub const HEADER_SIZE: usize = 0x50;
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct MemoryLayout {
65 pub slot_count: usize,
67 pub arena_bytes: usize,
69 pub event_arena_bytes: usize,
71}
72
73impl Default for MemoryLayout {
74 fn default() -> Self {
75 Self {
76 slot_count: DEFAULT_SLOT_COUNT,
77 arena_bytes: DEFAULT_ARENA_BYTES,
78 event_arena_bytes: DEFAULT_EVENT_ARENA_BYTES,
79 }
80 }
81}
82
83impl MemoryLayout {
84 #[inline]
86 pub fn ring_bytes(&self) -> usize {
87 self.slot_count * OPCODE_SIZE
88 }
89
90 #[inline]
93 pub fn event_ring_bytes(&self) -> usize {
94 self.slot_count * OPCODE_SIZE
95 }
96
97 pub fn total_bytes(&self) -> usize {
100 HEADER_SIZE
101 + self.ring_bytes()
102 + self.event_ring_bytes()
103 + self.arena_bytes
104 + self.event_arena_bytes
105 }
106
107 #[inline]
109 pub fn ring_offset(&self) -> usize {
110 HEADER_SIZE
111 }
112
113 #[inline]
115 pub fn event_ring_offset(&self) -> usize {
116 self.ring_offset() + self.ring_bytes()
117 }
118
119 #[inline]
121 pub fn arena_offset(&self) -> usize {
122 self.event_ring_offset() + self.event_ring_bytes()
123 }
124
125 #[inline]
128 pub fn event_arena_offset(&self) -> usize {
129 self.arena_offset() + self.arena_bytes
130 }
131
132 #[inline]
134 pub fn ring_mask(&self) -> usize {
135 debug_assert!(self.slot_count.is_power_of_two());
136 self.slot_count - 1
137 }
138}
139
140pub(crate) mod header {
145 use super::*;
146
147 #[inline]
148 pub(crate) fn get_u32(buf: &[u8], off: usize) -> u32 {
149 u32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
150 }
151
152 #[inline]
153 pub(crate) fn set_u32(buf: &mut [u8], off: usize, value: u32) {
154 buf[off..off + 4].copy_from_slice(&value.to_le_bytes());
155 }
156
157 #[inline]
158 pub(crate) fn get_u16(buf: &[u8], off: usize) -> u16 {
159 u16::from_le_bytes(buf[off..off + 2].try_into().unwrap())
160 }
161
162 pub(crate) fn init(buf: &mut [u8], layout: &MemoryLayout) {
164 buf[..HEADER_SIZE].fill(0);
165 set_u32(buf, OFF_MAGIC, MAGIC);
166 buf[OFF_VERSION..OFF_VERSION + 2].copy_from_slice(&PROTOCOL_VERSION.to_le_bytes());
167 set_u32(buf, OFF_RING_OFFSET, layout.ring_offset() as u32);
168 set_u32(buf, OFF_RING_BYTES, layout.ring_bytes() as u32);
169 set_u32(buf, OFF_SLOT_COUNT, layout.slot_count as u32);
170 set_u32(buf, OFF_ARENA_OFFSET, layout.arena_offset() as u32);
171 set_u32(buf, OFF_ARENA_BYTES, layout.arena_bytes as u32);
172 set_u32(buf, OFF_ARENA_CURSOR, 0);
173 set_u32(buf, OFF_READ_CURSOR, 0);
174 set_u32(buf, OFF_WRITE_CURSOR, 0);
175 set_u32(buf, OFF_FRAME_COUNT, 0);
176 set_u32(buf, OFF_EVENT_RING_OFFSET, layout.event_ring_offset() as u32);
177 set_u32(buf, OFF_EVENT_RING_BYTES, layout.event_ring_bytes() as u32);
178 set_u32(buf, OFF_EVENT_SLOT_COUNT, layout.slot_count as u32);
179 set_u32(buf, OFF_EVENT_READ_CURSOR, 0);
180 set_u32(buf, OFF_EVENT_WRITE_CURSOR, 0);
181 set_u32(buf, OFF_EVENT_ARENA_OFFSET, layout.event_arena_offset() as u32);
182 set_u32(buf, OFF_EVENT_ARENA_BYTES, layout.event_arena_bytes as u32);
183 set_u32(buf, OFF_EVENT_ARENA_CURSOR, 0);
184 }
185
186 #[allow(dead_code)]
189 pub(crate) fn validate(buf: &[u8], layout: &MemoryLayout) -> bool {
190 if buf.len() < HEADER_SIZE {
191 return false;
192 }
193 let magic = get_u32(buf, OFF_MAGIC);
194 if magic != MAGIC {
195 return false;
196 }
197 let version = get_u16(buf, OFF_VERSION);
198 if version != PROTOCOL_VERSION {
199 return false;
200 }
201 get_u32(buf, OFF_SLOT_COUNT) as usize == layout.slot_count
202 && get_u32(buf, OFF_ARENA_BYTES) as usize == layout.arena_bytes
203 && get_u32(buf, OFF_EVENT_RING_OFFSET) as usize == layout.event_ring_offset()
204 && get_u32(buf, OFF_EVENT_RING_BYTES) as usize == layout.event_ring_bytes()
205 && get_u32(buf, OFF_EVENT_SLOT_COUNT) as usize == layout.slot_count
206 && get_u32(buf, OFF_EVENT_ARENA_OFFSET) as usize == layout.event_arena_offset()
207 && get_u32(buf, OFF_EVENT_ARENA_BYTES) as usize == layout.event_arena_bytes
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use alloc::vec;
214 use super::*;
215
216 #[test]
217 fn header_size_is_80() {
218 assert_eq!(HEADER_SIZE, 80);
219 }
220
221 #[test]
222 fn validate_rejects_wrong_magic() {
223 let layout = MemoryLayout::default();
224 let mut buf = vec![0u8; layout.total_bytes()];
225 header::init(&mut buf, &layout);
226 buf[OFF_MAGIC] ^= 0xFF;
227 assert!(!header::validate(&buf, &layout));
228 }
229
230 #[test]
231 fn validate_rejects_wrong_version() {
232 let layout = MemoryLayout::default();
233 let mut buf = vec![0u8; layout.total_bytes()];
234 header::init(&mut buf, &layout);
235 buf[OFF_VERSION] = (PROTOCOL_VERSION + 1) as u8;
236 assert!(!header::validate(&buf, &layout));
237 }
238
239 #[test]
240 fn validate_rejects_mismatched_layout() {
241 let layout = MemoryLayout::default();
242 let mut buf = vec![0u8; layout.total_bytes()];
243 header::init(&mut buf, &layout);
244
245 let other = MemoryLayout {
247 slot_count: layout.slot_count,
248 arena_bytes: layout.arena_bytes + 4096,
249 event_arena_bytes: layout.event_arena_bytes,
250 };
251 assert!(!header::validate(&buf, &other));
252
253 let other = MemoryLayout {
255 slot_count: layout.slot_count,
256 arena_bytes: layout.arena_bytes,
257 event_arena_bytes: layout.event_arena_bytes + 4096,
258 };
259 assert!(!header::validate(&buf, &other));
260
261 let other = MemoryLayout {
263 slot_count: layout.slot_count / 2,
264 arena_bytes: layout.arena_bytes,
265 event_arena_bytes: layout.event_arena_bytes,
266 };
267 assert!(!header::validate(&buf, &other));
268 }
269
270 #[test]
271 fn validate_rejects_short_buffer() {
272 let layout = MemoryLayout::default();
273 let tiny = vec![0u8; HEADER_SIZE - 1];
274 assert!(!header::validate(&tiny, &layout));
275 }
276
277 #[test]
278 fn default_layout_is_contiguous() {
279 let layout = MemoryLayout::default();
280 assert_eq!(layout.ring_offset(), HEADER_SIZE);
281 assert_eq!(
282 layout.event_ring_offset(),
283 HEADER_SIZE + layout.ring_bytes()
284 );
285 assert_eq!(
286 layout.arena_offset(),
287 HEADER_SIZE + layout.ring_bytes() + layout.event_ring_bytes()
288 );
289 assert_eq!(
290 layout.event_arena_offset(),
291 HEADER_SIZE + layout.ring_bytes() + layout.event_ring_bytes() + layout.arena_bytes
292 );
293 assert_eq!(
294 layout.total_bytes(),
295 HEADER_SIZE
296 + layout.ring_bytes()
297 + layout.event_ring_bytes()
298 + layout.arena_bytes
299 + layout.event_arena_bytes
300 );
301 assert!(layout.slot_count.is_power_of_two());
302 }
303
304 #[test]
305 fn header_round_trips() {
306 let layout = MemoryLayout::default();
307 let mut buf = vec![0u8; layout.total_bytes()];
308 header::init(&mut buf, &layout);
309 assert!(header::validate(&buf, &layout));
310 assert_eq!(header::get_u32(&buf, OFF_SLOT_COUNT), layout.slot_count as u32);
311 assert_eq!(header::get_u32(&buf, OFF_ARENA_BYTES), layout.arena_bytes as u32);
312 assert_eq!(header::get_u32(&buf, OFF_ARENA_CURSOR), 0);
313 assert_eq!(header::get_u32(&buf, OFF_READ_CURSOR), 0);
314 assert_eq!(header::get_u32(&buf, OFF_WRITE_CURSOR), 0);
315 assert_eq!(header::get_u32(&buf, OFF_FRAME_COUNT), 0);
316 assert_eq!(
317 header::get_u32(&buf, OFF_EVENT_RING_OFFSET),
318 layout.event_ring_offset() as u32
319 );
320 assert_eq!(
321 header::get_u32(&buf, OFF_EVENT_RING_BYTES),
322 layout.event_ring_bytes() as u32
323 );
324 assert_eq!(
325 header::get_u32(&buf, OFF_EVENT_READ_CURSOR),
326 0
327 );
328 assert_eq!(
329 header::get_u32(&buf, OFF_EVENT_WRITE_CURSOR),
330 0
331 );
332 assert_eq!(
333 header::get_u32(&buf, OFF_EVENT_ARENA_OFFSET),
334 layout.event_arena_offset() as u32
335 );
336 assert_eq!(
337 header::get_u32(&buf, OFF_EVENT_ARENA_BYTES),
338 layout.event_arena_bytes as u32
339 );
340 assert_eq!(
341 header::get_u32(&buf, OFF_EVENT_ARENA_CURSOR),
342 0
343 );
344 }
345}