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 DestinationTooSmall {
73 required: usize,
75 provided: usize,
77 },
78 TopologyMismatch,
80 MissingRoute {
82 target: RoleId,
84 slot: u32,
86 },
87}
88
89impl fmt::Display for BindingError {
90 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(formatter, "mapping binding failed: {self:?}")
92 }
93}
94
95#[cfg(feature = "std")]
96impl std::error::Error for BindingError {}
97
98impl From<LayoutError> for BindingError {
99 fn from(value: LayoutError) -> Self {
100 Self::Layout(value)
101 }
102}
103
104impl From<SlotError> for BindingError {
105 fn from(value: SlotError) -> Self {
106 Self::Slot(value)
107 }
108}
109
110unsafe fn volatile_copy(source: *const u8, destination: *mut u8, len: usize) {
117 for index in 0..len {
118 unsafe {
120 destination
121 .add(index)
122 .write(core::ptr::read_volatile(source.add(index)));
123 }
124 }
125}
126
127pub struct ReaderRegion<M> {
129 mapping: M,
130 layout: ValidatedRegionLayout,
131 topology: RegionSetLayout,
132}
133
134impl<M: ReadOnlyMapping> ReaderRegion<M> {
135 pub fn new(
140 mapping: M,
141 layout: ValidatedRegionLayout,
142 topology: RegionSetLayout,
143 ) -> Result<Self, (M, BindingError)> {
144 if let Err(error) = validate_mapping_size(mapping.len(), &layout) {
145 return Err((mapping, error));
146 }
147 if let Err(error) = validate_topology(&layout, &topology) {
148 return Err((mapping, error));
149 }
150 Ok(Self {
151 mapping,
152 layout,
153 topology,
154 })
155 }
156
157 pub fn slot(&self, slot: u32) -> Result<ReaderSlot<'_>, BindingError> {
159 let binding = self.layout.reader_slot_binding(slot)?;
160 let range = self.layout.slot_range(slot)?;
161 let header = record::<SlotMetadata, _>(
162 &self.mapping,
163 self.mapping.base(),
164 range.start,
165 range.len(),
166 )?;
167 Ok(unsafe { ReaderSlot::bind(header, binding) }?)
170 }
171
172 pub fn copy_payload(&self, slot: u32, expected_sequence: u64) -> Result<Vec<u8>, BindingError> {
177 let observation = self.slot(slot)?.observe(expected_sequence)?;
178 let range = self
179 .layout
180 .slot_payload_range(slot, observation.payload_len())?;
181 let mut owned = Vec::<u8>::new();
182 owned
183 .try_reserve_exact(range.len())
184 .map_err(|_| BindingError::AllocationFailed)?;
185 unsafe {
190 owned.set_len(range.len());
191 volatile_copy(
192 self.mapping.base().as_ptr().add(range.start),
193 owned.as_mut_ptr(),
194 range.len(),
195 );
196 }
197 self.slot(slot)?.recheck(observation)?;
198 Ok(owned)
199 }
200
201 pub fn copy_payload_into(
208 &self,
209 slot: u32,
210 expected_sequence: u64,
211 destination: &mut [u8],
212 ) -> Result<usize, BindingError> {
213 let observation = self.slot(slot)?.observe(expected_sequence)?;
214 let range = self
215 .layout
216 .slot_payload_range(slot, observation.payload_len())?;
217 if destination.len() < range.len() {
218 return Err(BindingError::DestinationTooSmall {
219 required: range.len(),
220 provided: destination.len(),
221 });
222 }
223 unsafe {
226 volatile_copy(
227 self.mapping.base().as_ptr().add(range.start),
228 destination.as_mut_ptr(),
229 range.len(),
230 );
231 }
232 self.slot(slot)?.recheck(observation)?;
233 Ok(range.len())
234 }
235
236 pub fn into_mapping(self) -> M {
238 self.mapping
239 }
240
241 pub fn acknowledgement(
243 &self,
244 target: RoleId,
245 slot: u32,
246 ) -> Result<AcknowledgementReader<'_>, BindingError> {
247 let route = self
248 .topology
249 .acknowledgement_route(target, slot)
250 .ok_or(BindingError::MissingRoute { target, slot })?;
251 let binding = self.layout.acknowledgement_reader_binding(route)?;
252 let range = self.layout.acknowledgement_range(route.cell_index())?;
253 let cell = record::<AcknowledgementCell, _>(
254 &self.mapping,
255 self.mapping.base(),
256 range.start,
257 range.len(),
258 )?;
259 Ok(unsafe { AcknowledgementReader::bind(cell, binding) })
261 }
262}
263
264pub struct WriterRegion<M> {
266 mapping: M,
267 layout: ValidatedRegionLayout,
268 topology: RegionSetLayout,
269}
270
271impl<M: SoleWriterMapping> WriterRegion<M> {
272 pub fn new(
277 mapping: M,
278 layout: ValidatedRegionLayout,
279 topology: RegionSetLayout,
280 ) -> Result<Self, (M, BindingError)> {
281 if let Err(error) = validate_mapping_size(mapping.len(), &layout) {
282 return Err((mapping, error));
283 }
284 if let Err(error) = validate_topology(&layout, &topology) {
285 return Err((mapping, error));
286 }
287 Ok(Self {
288 mapping,
289 layout,
290 topology,
291 })
292 }
293
294 pub fn into_mapping(self) -> M {
296 self.mapping
297 }
298
299 pub fn slot(&mut self, slot: u32) -> Result<WriterSlot<'_>, BindingError> {
301 let target = self.layout.role();
302 let route = self
303 .topology
304 .acknowledgement_route(target, slot)
305 .ok_or(BindingError::MissingRoute { target, slot })?;
306 let binding = self.layout.writer_slot_binding(route)?;
307 let range = self.layout.slot_range(route.slot_index())?;
308 let header = record::<SlotMetadata, _>(
309 &self.mapping,
310 self.mapping.base(),
311 range.start,
312 range.len(),
313 )?;
314 Ok(unsafe { WriterSlot::bind(header, binding) }?)
317 }
318
319 pub fn publish(
321 &mut self,
322 slot: u32,
323 sequence: u64,
324 acknowledgement: Option<AcknowledgementObservation>,
325 payload: &[u8],
326 ) -> Result<(), BindingError> {
327 let payload_len =
328 u32::try_from(payload.len()).map_err(|_| BindingError::PayloadLengthOverflow)?;
329 let range = self.layout.slot_payload_range(slot, payload_len)?;
330 let base = self.mapping.base();
331 let mut bound_slot = self.slot(slot)?;
332 let reservation = bound_slot.prepare_publish(sequence, acknowledgement)?;
333 unsafe {
336 core::ptr::copy_nonoverlapping(
337 payload.as_ptr(),
338 base.as_ptr().add(range.start),
339 payload.len(),
340 );
341 }
342 reservation.publish(payload_len)?;
343 Ok(())
344 }
345
346 pub fn acknowledgement(
348 &mut self,
349 target: RoleId,
350 slot: u32,
351 ) -> Result<AcknowledgementWriter<'_>, BindingError> {
352 let route = self
353 .topology
354 .acknowledgement_route(target, slot)
355 .ok_or(BindingError::MissingRoute { target, slot })?;
356 let binding = self.layout.acknowledgement_writer_binding(route)?;
357 let range = self.layout.acknowledgement_range(route.cell_index())?;
358 let cell = record::<AcknowledgementCell, _>(
359 &self.mapping,
360 self.mapping.base(),
361 range.start,
362 range.len(),
363 )?;
364 Ok(unsafe { AcknowledgementWriter::bind(cell, binding) })
366 }
367}
368
369fn validate_mapping_size(
370 actual: usize,
371 layout: &ValidatedRegionLayout,
372) -> Result<(), BindingError> {
373 if actual == layout.mapping_size() {
374 Ok(())
375 } else {
376 Err(BindingError::MappingSizeMismatch {
377 expected: layout.mapping_size(),
378 actual,
379 })
380 }
381}
382
383fn validate_topology(
384 layout: &ValidatedRegionLayout,
385 topology: &RegionSetLayout,
386) -> Result<(), BindingError> {
387 if layout.matches_topology(topology) {
388 Ok(())
389 } else {
390 Err(BindingError::TopologyMismatch)
391 }
392}
393
394fn record<T, M>(
395 _owner: &M,
396 base: NonNull<u8>,
397 offset: usize,
398 available: usize,
399) -> Result<&T, BindingError> {
400 if available < size_of::<T>() {
401 return Err(BindingError::Layout(LayoutError::RangeOutOfBounds));
402 }
403 let pointer = unsafe { base.as_ptr().add(offset) }.cast::<T>();
407 if !(pointer as usize).is_multiple_of(align_of::<T>()) {
408 return Err(BindingError::MisalignedRecord);
409 }
410 Ok(unsafe { &*pointer })
413}
414
415#[cfg(test)]
416#[path = "mapping_test.rs"]
417mod tests;