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::<u8>::new();
151 owned
152 .try_reserve_exact(range.len())
153 .map_err(|_| BindingError::AllocationFailed)?;
154 unsafe {
159 owned.set_len(range.len());
160 let source = self.mapping.base().as_ptr().add(range.start);
161 let destination = owned.as_mut_ptr();
162 for index in 0..range.len() {
163 destination
164 .add(index)
165 .write(core::ptr::read_volatile(source.add(index)));
166 }
167 }
168 self.slot(slot)?.recheck(observation)?;
169 Ok(owned)
170 }
171
172 pub fn acknowledgement(
174 &self,
175 target: RoleId,
176 slot: u32,
177 ) -> Result<AcknowledgementReader<'_>, BindingError> {
178 let route = self
179 .topology
180 .acknowledgement_route(target, slot)
181 .ok_or(BindingError::MissingRoute { target, slot })?;
182 let binding = self.layout.acknowledgement_reader_binding(route)?;
183 let range = self.layout.acknowledgement_range(route.cell_index())?;
184 let cell = record::<AcknowledgementCell, _>(
185 &self.mapping,
186 self.mapping.base(),
187 range.start,
188 range.len(),
189 )?;
190 Ok(unsafe { AcknowledgementReader::bind(cell, binding) })
192 }
193}
194
195pub struct WriterRegion<M> {
197 mapping: M,
198 layout: ValidatedRegionLayout,
199 topology: RegionSetLayout,
200}
201
202impl<M: SoleWriterMapping> WriterRegion<M> {
203 pub fn new(
205 mapping: M,
206 layout: ValidatedRegionLayout,
207 topology: RegionSetLayout,
208 ) -> Result<Self, BindingError> {
209 validate_mapping_size(mapping.len(), &layout)?;
210 validate_topology(&layout, &topology)?;
211 Ok(Self {
212 mapping,
213 layout,
214 topology,
215 })
216 }
217
218 pub fn slot(&mut self, slot: u32) -> Result<WriterSlot<'_>, BindingError> {
220 let target = self.layout.role();
221 let route = self
222 .topology
223 .acknowledgement_route(target, slot)
224 .ok_or(BindingError::MissingRoute { target, slot })?;
225 let binding = self.layout.writer_slot_binding(route)?;
226 let range = self.layout.slot_range(route.slot_index())?;
227 let header = record::<SlotMetadata, _>(
228 &self.mapping,
229 self.mapping.base(),
230 range.start,
231 range.len(),
232 )?;
233 Ok(unsafe { WriterSlot::bind(header, binding) }?)
236 }
237
238 pub fn publish(
240 &mut self,
241 slot: u32,
242 sequence: u64,
243 acknowledgement: Option<AcknowledgementObservation>,
244 payload: &[u8],
245 ) -> Result<(), BindingError> {
246 let payload_len =
247 u32::try_from(payload.len()).map_err(|_| BindingError::PayloadLengthOverflow)?;
248 let range = self.layout.slot_payload_range(slot, payload_len)?;
249 let base = self.mapping.base();
250 let mut bound_slot = self.slot(slot)?;
251 let reservation = bound_slot.prepare_publish(sequence, acknowledgement)?;
252 unsafe {
255 core::ptr::copy_nonoverlapping(
256 payload.as_ptr(),
257 base.as_ptr().add(range.start),
258 payload.len(),
259 );
260 }
261 reservation.publish(payload_len)?;
262 Ok(())
263 }
264
265 pub fn acknowledgement(
267 &mut self,
268 target: RoleId,
269 slot: u32,
270 ) -> Result<AcknowledgementWriter<'_>, BindingError> {
271 let route = self
272 .topology
273 .acknowledgement_route(target, slot)
274 .ok_or(BindingError::MissingRoute { target, slot })?;
275 let binding = self.layout.acknowledgement_writer_binding(route)?;
276 let range = self.layout.acknowledgement_range(route.cell_index())?;
277 let cell = record::<AcknowledgementCell, _>(
278 &self.mapping,
279 self.mapping.base(),
280 range.start,
281 range.len(),
282 )?;
283 Ok(unsafe { AcknowledgementWriter::bind(cell, binding) })
285 }
286}
287
288fn validate_mapping_size(
289 actual: usize,
290 layout: &ValidatedRegionLayout,
291) -> Result<(), BindingError> {
292 if actual == layout.mapping_size() {
293 Ok(())
294 } else {
295 Err(BindingError::MappingSizeMismatch {
296 expected: layout.mapping_size(),
297 actual,
298 })
299 }
300}
301
302fn validate_topology(
303 layout: &ValidatedRegionLayout,
304 topology: &RegionSetLayout,
305) -> Result<(), BindingError> {
306 if layout.matches_topology(topology) {
307 Ok(())
308 } else {
309 Err(BindingError::TopologyMismatch)
310 }
311}
312
313fn record<T, M>(
314 _owner: &M,
315 base: NonNull<u8>,
316 offset: usize,
317 available: usize,
318) -> Result<&T, BindingError> {
319 if available < size_of::<T>() {
320 return Err(BindingError::Layout(LayoutError::RangeOutOfBounds));
321 }
322 let pointer = unsafe { base.as_ptr().add(offset) }.cast::<T>();
326 if !(pointer as usize).is_multiple_of(align_of::<T>()) {
327 return Err(BindingError::MisalignedRecord);
328 }
329 Ok(unsafe { &*pointer })
332}
333
334#[cfg(test)]
335#[path = "mapping_test.rs"]
336mod tests;