1use alloc::vec::Vec;
4use core::fmt;
5use core::mem::{align_of, size_of};
6use core::ptr::NonNull;
7
8use crate::layout::{LayoutError, RegionSetLayout, RoleId, ValidatedRegionLayout};
9use crate::slot::{
10 AcknowledgementCell, AcknowledgementObservation, AcknowledgementReader, AcknowledgementWriter,
11 ReaderSlot, SlotError, SlotMetadata, WriterSlot,
12};
13
14pub unsafe trait ReadOnlyMapping {
23 fn base(&self) -> NonNull<u8>;
25 fn len(&self) -> usize;
27 fn is_empty(&self) -> bool {
29 self.len() == 0
30 }
31}
32
33pub unsafe trait SoleWriterMapping {
41 fn base(&self) -> NonNull<u8>;
43 fn len(&self) -> usize;
45 fn is_empty(&self) -> bool {
47 self.len() == 0
48 }
49}
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
53pub enum BindingError {
54 MappingSizeMismatch {
56 expected: usize,
58 actual: usize,
60 },
61 MisalignedRecord,
63 Layout(LayoutError),
65 Slot(SlotError),
67 AllocationFailed,
69 PayloadLengthOverflow,
71 TopologyMismatch,
73 MissingRoute {
75 target: RoleId,
77 slot: u32,
79 },
80}
81
82impl fmt::Display for BindingError {
83 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(formatter, "mapping binding failed: {self:?}")
85 }
86}
87
88#[cfg(feature = "std")]
89impl std::error::Error for BindingError {}
90
91impl From<LayoutError> for BindingError {
92 fn from(value: LayoutError) -> Self {
93 Self::Layout(value)
94 }
95}
96
97impl From<SlotError> for BindingError {
98 fn from(value: SlotError) -> Self {
99 Self::Slot(value)
100 }
101}
102
103pub struct ReaderRegion<M> {
105 mapping: M,
106 layout: ValidatedRegionLayout,
107 topology: RegionSetLayout,
108}
109
110impl<M: ReadOnlyMapping> ReaderRegion<M> {
111 pub fn new(
113 mapping: M,
114 layout: ValidatedRegionLayout,
115 topology: RegionSetLayout,
116 ) -> Result<Self, BindingError> {
117 validate_mapping_size(mapping.len(), &layout)?;
118 validate_topology(&layout, &topology)?;
119 Ok(Self {
120 mapping,
121 layout,
122 topology,
123 })
124 }
125
126 pub fn slot(&self, slot: u32) -> Result<ReaderSlot<'_>, BindingError> {
128 let binding = self.layout.reader_slot_binding(slot)?;
129 let range = self.layout.slot_range(slot)?;
130 let header = record::<SlotMetadata, _>(
131 &self.mapping,
132 self.mapping.base(),
133 range.start,
134 range.len(),
135 )?;
136 Ok(unsafe { ReaderSlot::bind(header, binding) }?)
139 }
140
141 pub fn copy_payload(&self, slot: u32, expected_sequence: u64) -> Result<Vec<u8>, BindingError> {
146 let observation = self.slot(slot)?.observe(expected_sequence)?;
147 let range = self
148 .layout
149 .slot_payload_range(slot, observation.payload_len())?;
150 let mut owned = Vec::new();
151 owned
152 .try_reserve_exact(range.len())
153 .map_err(|_| BindingError::AllocationFailed)?;
154 unsafe {
157 owned.set_len(range.len());
158 core::ptr::copy_nonoverlapping(
159 self.mapping.base().as_ptr().add(range.start),
160 owned.as_mut_ptr(),
161 range.len(),
162 );
163 }
164 self.slot(slot)?.recheck(observation)?;
165 Ok(owned)
166 }
167
168 pub fn acknowledgement(
170 &self,
171 target: RoleId,
172 slot: u32,
173 ) -> Result<AcknowledgementReader<'_>, BindingError> {
174 let route = self
175 .topology
176 .acknowledgement_route(target, slot)
177 .ok_or(BindingError::MissingRoute { target, slot })?;
178 let binding = self.layout.acknowledgement_reader_binding(route)?;
179 let range = self.layout.acknowledgement_range(route.cell_index())?;
180 let cell = record::<AcknowledgementCell, _>(
181 &self.mapping,
182 self.mapping.base(),
183 range.start,
184 range.len(),
185 )?;
186 Ok(unsafe { AcknowledgementReader::bind(cell, binding) })
188 }
189}
190
191pub struct WriterRegion<M> {
193 mapping: M,
194 layout: ValidatedRegionLayout,
195 topology: RegionSetLayout,
196}
197
198impl<M: SoleWriterMapping> WriterRegion<M> {
199 pub fn new(
201 mapping: M,
202 layout: ValidatedRegionLayout,
203 topology: RegionSetLayout,
204 ) -> Result<Self, BindingError> {
205 validate_mapping_size(mapping.len(), &layout)?;
206 validate_topology(&layout, &topology)?;
207 Ok(Self {
208 mapping,
209 layout,
210 topology,
211 })
212 }
213
214 pub fn slot(&mut self, slot: u32) -> Result<WriterSlot<'_>, BindingError> {
216 let target = self.layout.role();
217 let route = self
218 .topology
219 .acknowledgement_route(target, slot)
220 .ok_or(BindingError::MissingRoute { target, slot })?;
221 let binding = self.layout.writer_slot_binding(route)?;
222 let range = self.layout.slot_range(route.slot_index())?;
223 let header = record::<SlotMetadata, _>(
224 &self.mapping,
225 self.mapping.base(),
226 range.start,
227 range.len(),
228 )?;
229 Ok(unsafe { WriterSlot::bind(header, binding) }?)
232 }
233
234 pub fn publish(
236 &mut self,
237 slot: u32,
238 sequence: u64,
239 acknowledgement: Option<AcknowledgementObservation>,
240 payload: &[u8],
241 ) -> Result<(), BindingError> {
242 let payload_len =
243 u32::try_from(payload.len()).map_err(|_| BindingError::PayloadLengthOverflow)?;
244 let range = self.layout.slot_payload_range(slot, payload_len)?;
245 let base = self.mapping.base();
246 let mut bound_slot = self.slot(slot)?;
247 let reservation = bound_slot.prepare_publish(sequence, acknowledgement)?;
248 unsafe {
251 core::ptr::copy_nonoverlapping(
252 payload.as_ptr(),
253 base.as_ptr().add(range.start),
254 payload.len(),
255 );
256 }
257 reservation.publish(payload_len)?;
258 Ok(())
259 }
260
261 pub fn acknowledgement(
263 &mut self,
264 target: RoleId,
265 slot: u32,
266 ) -> Result<AcknowledgementWriter<'_>, BindingError> {
267 let route = self
268 .topology
269 .acknowledgement_route(target, slot)
270 .ok_or(BindingError::MissingRoute { target, slot })?;
271 let binding = self.layout.acknowledgement_writer_binding(route)?;
272 let range = self.layout.acknowledgement_range(route.cell_index())?;
273 let cell = record::<AcknowledgementCell, _>(
274 &self.mapping,
275 self.mapping.base(),
276 range.start,
277 range.len(),
278 )?;
279 Ok(unsafe { AcknowledgementWriter::bind(cell, binding) })
281 }
282}
283
284fn validate_mapping_size(
285 actual: usize,
286 layout: &ValidatedRegionLayout,
287) -> Result<(), BindingError> {
288 if actual == layout.mapping_size() {
289 Ok(())
290 } else {
291 Err(BindingError::MappingSizeMismatch {
292 expected: layout.mapping_size(),
293 actual,
294 })
295 }
296}
297
298fn validate_topology(
299 layout: &ValidatedRegionLayout,
300 topology: &RegionSetLayout,
301) -> Result<(), BindingError> {
302 if layout.matches_topology(topology) {
303 Ok(())
304 } else {
305 Err(BindingError::TopologyMismatch)
306 }
307}
308
309fn record<T, M>(
310 _owner: &M,
311 base: NonNull<u8>,
312 offset: usize,
313 available: usize,
314) -> Result<&T, BindingError> {
315 if available < size_of::<T>() {
316 return Err(BindingError::Layout(LayoutError::RangeOutOfBounds));
317 }
318 let pointer = unsafe { base.as_ptr().add(offset) }.cast::<T>();
322 if !(pointer as usize).is_multiple_of(align_of::<T>()) {
323 return Err(BindingError::MisalignedRecord);
324 }
325 Ok(unsafe { &*pointer })
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333 use crate::layout::{
334 AcknowledgementRouteSpec, Endpoint, LayoutLimits, RegionSetLayout, RegionSpec, RoleId,
335 ValidationExpectations,
336 };
337 use std::alloc::{Layout, alloc_zeroed, dealloc};
338
339 struct Allocation {
340 base: NonNull<u8>,
341 len: usize,
342 }
343
344 impl Allocation {
345 fn new(len: usize) -> Self {
346 let layout = Layout::from_size_align(len, 64).unwrap();
347 let base = NonNull::new(unsafe { alloc_zeroed(layout) }).unwrap();
348 Self { base, len }
349 }
350 fn bytes_mut(&mut self) -> &mut [u8] {
351 unsafe { core::slice::from_raw_parts_mut(self.base.as_ptr(), self.len) }
352 }
353 fn bytes(&self) -> &[u8] {
354 unsafe { core::slice::from_raw_parts(self.base.as_ptr(), self.len) }
355 }
356 }
357 impl Drop for Allocation {
358 fn drop(&mut self) {
359 unsafe {
360 dealloc(
361 self.base.as_ptr(),
362 Layout::from_size_align(self.len, 64).unwrap(),
363 )
364 }
365 }
366 }
367
368 struct ReaderWitness<'a>(&'a Allocation);
369 struct WriterWitness<'a>(&'a mut Allocation);
370 unsafe impl ReadOnlyMapping for ReaderWitness<'_> {
371 fn base(&self) -> NonNull<u8> {
372 self.0.base
373 }
374 fn len(&self) -> usize {
375 self.0.len
376 }
377 }
378 unsafe impl SoleWriterMapping for WriterWitness<'_> {
379 fn base(&self) -> NonNull<u8> {
380 self.0.base
381 }
382 fn len(&self) -> usize {
383 self.0.len
384 }
385 }
386
387 #[test]
388 fn initialize_validate_bind_publish_recheck_and_acknowledge() {
389 let producer = RoleId::new(1).unwrap();
390 let acknowledger = RoleId::new(2).unwrap();
391 let specs = [
392 RegionSpec {
393 role: producer,
394 writer: Endpoint::Initiator,
395 slot_count: 1,
396 payload_bytes: 16,
397 acknowledgement_count: 1,
398 },
399 RegionSpec {
400 role: acknowledger,
401 writer: Endpoint::Responder,
402 slot_count: 1,
403 payload_bytes: 16,
404 acknowledgement_count: 1,
405 },
406 ];
407 let route_specs = [
408 AcknowledgementRouteSpec {
409 owner: acknowledger,
410 target: producer,
411 slot_index: 0,
412 cell_index: 0,
413 },
414 AcknowledgementRouteSpec {
415 owner: producer,
416 target: acknowledger,
417 slot_index: 0,
418 cell_index: 0,
419 },
420 ];
421 let limits = LayoutLimits {
422 maximum_mapping_size: 4096,
423 maximum_slot_count: 2,
424 maximum_acknowledgement_count: 2,
425 maximum_payload_bytes: 64,
426 };
427 let set = RegionSetLayout::calculate([7; 32], 9, &specs, &route_specs, limits).unwrap();
428 let producer_layout = set.region(producer).unwrap();
429 let mut producer_memory = Allocation::new(producer_layout.total_size() as usize);
430 producer_layout
431 .encode_into(producer_memory.bytes_mut())
432 .unwrap();
433 let producer_validated = unsafe {
434 ValidatedRegionLayout::validate(
435 producer_memory.bytes(),
436 ValidationExpectations {
437 schema_id: [7; 32],
438 generation: 9,
439 role: producer,
440 writer: Endpoint::Initiator,
441 maximum_mapping_size: 4096,
442 },
443 &set,
444 )
445 }
446 .unwrap();
447
448 let ack_layout = set.region(acknowledger).unwrap();
449 let mut ack_memory = Allocation::new(ack_layout.total_size() as usize);
450 ack_layout.encode_into(ack_memory.bytes_mut()).unwrap();
451 let ack_validated = unsafe {
452 ValidatedRegionLayout::validate(
453 ack_memory.bytes(),
454 ValidationExpectations {
455 schema_id: [7; 32],
456 generation: 9,
457 role: acknowledger,
458 writer: Endpoint::Responder,
459 maximum_mapping_size: 4096,
460 },
461 &set,
462 )
463 }
464 .unwrap();
465
466 {
467 let mut writer = WriterRegion::new(
468 WriterWitness(&mut producer_memory),
469 producer_validated.clone(),
470 set.clone(),
471 )
472 .unwrap();
473 writer.publish(0, 1, None, b"ping").unwrap();
474 }
475 let reader = ReaderRegion::new(
476 ReaderWitness(&producer_memory),
477 producer_validated,
478 set.clone(),
479 )
480 .unwrap();
481 let observation = reader.slot(0).unwrap().observe(1).unwrap();
482 reader.slot(0).unwrap().recheck(observation).unwrap();
483 assert_eq!(reader.copy_payload(0, 1).unwrap(), b"ping");
484
485 {
486 let mut ack_writer = WriterRegion::new(
487 WriterWitness(&mut ack_memory),
488 ack_validated.clone(),
489 set.clone(),
490 )
491 .unwrap();
492 ack_writer
493 .acknowledgement(producer, 0)
494 .unwrap()
495 .acknowledge(observation)
496 .unwrap();
497 }
498 let ack_reader = ReaderRegion::new(ReaderWitness(&ack_memory), ack_validated, set).unwrap();
499 let acknowledged = ack_reader.acknowledgement(producer, 0).unwrap().observe();
500 assert_eq!(acknowledged.sequence(), 1);
501 assert_eq!(acknowledged.slot_index(), 0);
502 assert_eq!(acknowledged.cell_index(), 0);
503 }
504}