Skip to main content

native_ipc_core/
slot.rs

1//! Generation-bound, role-bound slot and acknowledgement capabilities.
2
3use core::cell::{Cell, UnsafeCell};
4use core::fmt;
5use core::marker::PhantomData;
6use core::sync::atomic::{AtomicU32, AtomicU64, Ordering, fence};
7
8use crate::layout::{AcknowledgementRoute, RoleId};
9
10#[cfg(not(target_has_atomic = "64"))]
11compile_error!("native-ipc-core requires lock-free 64-bit atomic support");
12
13/// Bytes occupied by one cache-line-aligned slot metadata record.
14pub const SLOT_HEADER_SIZE: u64 = 64;
15/// Bytes occupied by one cache-line-aligned acknowledgement cell.
16pub const ACKNOWLEDGEMENT_CELL_SIZE: u64 = 64;
17
18/// Writer-owned atomic publication metadata for one fixed-capacity slot.
19///
20/// This type is an in-memory concurrency record, not a serializable Rust wire
21/// layout. A region layout fixes its offsets and requires 64-bit atomics.
22#[repr(C, align(64))]
23#[derive(Debug)]
24pub struct SlotMetadata {
25    generation: AtomicU64,
26    payload_len: AtomicU32,
27    reserved_word: UnsafeCell<u32>,
28    published_sequence: AtomicU64,
29    reserved: UnsafeCell<[u8; 40]>,
30}
31
32impl SlotMetadata {
33    /// Creates unpublished metadata in quiescent, process-local storage.
34    pub const fn new(generation: u64) -> Self {
35        Self {
36            generation: AtomicU64::new(generation),
37            payload_len: AtomicU32::new(0),
38            reserved_word: UnsafeCell::new(0),
39            published_sequence: AtomicU64::new(0),
40            reserved: UnsafeCell::new([0; 40]),
41        }
42    }
43
44    /// Reinitializes metadata before any peer can access the mapping.
45    ///
46    /// # Safety
47    ///
48    /// No peer process or concurrent thread may access this slot until the
49    /// initialization and all associated payload initialization complete.
50    pub unsafe fn initialize(&mut self, generation: u64) -> Result<(), SlotError> {
51        if generation == 0 {
52            return Err(SlotError::ZeroGeneration);
53        }
54        *self.generation.get_mut() = generation;
55        *self.payload_len.get_mut() = 0;
56        // SAFETY: `&mut self` and the function contract exclude all aliases.
57        unsafe { *self.reserved_word.get() = 0 };
58        *self.published_sequence.get_mut() = 0;
59        // SAFETY: `&mut self` and the function contract exclude all aliases.
60        unsafe { *self.reserved.get() = [0; 40] };
61        Ok(())
62    }
63}
64
65// SAFETY: live fields are accessed only through atomics. `UnsafeCell` makes
66// peer mutation of padding explicit; protocol code never reads that padding
67// after quiescent validation. Peers must use compatible aligned atomics for
68// atomic fields.
69unsafe impl Sync for SlotMetadata {}
70
71/// A single writer-owned atomic acknowledgement sequence.
72#[repr(C, align(64))]
73pub struct AcknowledgementCell {
74    sequence: AtomicU64,
75    reserved: UnsafeCell<[u8; 56]>,
76}
77
78impl AcknowledgementCell {
79    /// Creates a zero/unpublished cell in quiescent storage.
80    pub const fn new() -> Self {
81        Self {
82            sequence: AtomicU64::new(0),
83            reserved: UnsafeCell::new([0; 56]),
84        }
85    }
86
87    /// Reinitializes this cell before peer access.
88    ///
89    /// # Safety
90    ///
91    /// No peer process or concurrent thread may access this cell.
92    pub unsafe fn initialize(&mut self) {
93        *self.sequence.get_mut() = 0;
94        // SAFETY: `&mut self` and the function contract exclude all aliases.
95        unsafe { *self.reserved.get() = [0; 56] };
96    }
97}
98
99// SAFETY: `sequence` is atomic and externally mutable padding is never read
100// after quiescent validation.
101unsafe impl Sync for AcknowledgementCell {}
102
103impl Default for AcknowledgementCell {
104    fn default() -> Self {
105        Self::new()
106    }
107}
108
109/// Immutable facts for a slot in a validated writable mapping.
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub struct WriterSlotBinding(SlotBinding);
112
113impl WriterSlotBinding {
114    pub(crate) const fn validated(
115        role: RoleId,
116        generation: u64,
117        payload_capacity: u32,
118        slot_index: u32,
119        slot_count: u32,
120        acknowledgement_owner: RoleId,
121        acknowledgement_cell_index: u32,
122    ) -> Self {
123        Self(SlotBinding {
124            role,
125            generation,
126            payload_capacity,
127            slot_index,
128            slot_count,
129            acknowledgement_owner: Some(acknowledgement_owner),
130            acknowledgement_cell_index: Some(acknowledgement_cell_index),
131        })
132    }
133}
134
135/// Immutable facts for a slot in a validated read-only mapping.
136#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137pub struct ReaderSlotBinding(SlotBinding);
138
139impl ReaderSlotBinding {
140    pub(crate) const fn validated(
141        role: RoleId,
142        generation: u64,
143        payload_capacity: u32,
144        slot_index: u32,
145        slot_count: u32,
146    ) -> Self {
147        Self(SlotBinding {
148            role,
149            generation,
150            payload_capacity,
151            slot_index,
152            slot_count,
153            acknowledgement_owner: None,
154            acknowledgement_cell_index: None,
155        })
156    }
157}
158
159#[derive(Clone, Copy, Debug, Eq, PartialEq)]
160struct SlotBinding {
161    role: RoleId,
162    generation: u64,
163    payload_capacity: u32,
164    slot_index: u32,
165    slot_count: u32,
166    acknowledgement_owner: Option<RoleId>,
167    acknowledgement_cell_index: Option<u32>,
168}
169
170/// Sole-writer capability bound to a validated writable mapping.
171pub struct WriterSlot<'a> {
172    header: &'a SlotMetadata,
173    binding: SlotBinding,
174    _not_sync: PhantomData<Cell<()>>,
175}
176
177impl<'a> WriterSlot<'a> {
178    /// Binds atomic metadata reached through the sole writer's mapping.
179    ///
180    /// # Safety
181    ///
182    /// `header` must reside at the checked slot offset represented by
183    /// `binding`. This process must be the only holder of a writable mapping,
184    /// and the mapping must remain live for `'a`. No other writer capability
185    /// may be bound for this slot while this value exists.
186    pub unsafe fn bind(
187        header: &'a SlotMetadata,
188        binding: WriterSlotBinding,
189    ) -> Result<Self, SlotError> {
190        validate_bound_generation(header, binding.0.generation)?;
191        Ok(Self {
192            header,
193            binding: binding.0,
194            _not_sync: PhantomData,
195        })
196    }
197
198    /// Checks sequence/slot/reuse invariants before payload mutation begins.
199    pub fn prepare_publish(
200        &mut self,
201        sequence: u64,
202        acknowledgement: Option<AcknowledgementObservation>,
203    ) -> Result<PublishReservation<'_>, SlotError> {
204        validate_bound_generation(self.header, self.binding.generation)?;
205        validate_sequence_slot(self.binding, sequence)?;
206        let current = self.header.published_sequence.load(Ordering::Relaxed);
207        if current == 0 {
208            let expected = u64::from(self.binding.slot_index) + 1;
209            if sequence != expected {
210                return Err(SlotError::UnexpectedFirstSequence {
211                    expected,
212                    actual: sequence,
213                });
214            }
215        } else {
216            let expected = current
217                .checked_add(u64::from(self.binding.slot_count))
218                .ok_or(SlotError::SequenceWrap)?;
219            if sequence != expected {
220                return Err(SlotError::UnexpectedNextSequence {
221                    expected,
222                    actual: sequence,
223                });
224            }
225            let acknowledgement =
226                acknowledgement.ok_or(SlotError::MissingAcknowledgement { sequence: current })?;
227            if acknowledgement.target != self.binding.role {
228                return Err(SlotError::WrongAcknowledgementTarget);
229            }
230            if acknowledgement.owner != self.binding.acknowledgement_owner.unwrap() {
231                return Err(SlotError::WrongAcknowledgementOwner);
232            }
233            if acknowledgement.slot_index != self.binding.slot_index {
234                return Err(SlotError::WrongAcknowledgementSlot);
235            }
236            if acknowledgement.cell_index != self.binding.acknowledgement_cell_index.unwrap() {
237                return Err(SlotError::WrongAcknowledgementCell);
238            }
239            if acknowledgement.generation != self.binding.generation {
240                return Err(SlotError::StaleAcknowledgementGeneration);
241            }
242            if acknowledgement.sequence < current {
243                return Err(SlotError::LaggingAcknowledgement {
244                    expected: current,
245                    actual: acknowledgement.sequence,
246                });
247            }
248            if acknowledgement.sequence > current {
249                return Err(SlotError::FutureAcknowledgement {
250                    expected: current,
251                    actual: acknowledgement.sequence,
252                });
253            }
254        }
255        Ok(PublishReservation {
256            header: self.header,
257            sequence,
258            capacity: self.binding.payload_capacity,
259            _exclusive: PhantomData,
260        })
261    }
262}
263
264/// Acquire-only capability bound to a validated read-only mapping.
265pub struct ReaderSlot<'a> {
266    header: &'a SlotMetadata,
267    binding: SlotBinding,
268}
269
270impl<'a> ReaderSlot<'a> {
271    /// Binds atomic metadata reached through a read-only native mapping.
272    ///
273    /// # Safety
274    ///
275    /// `header` must reside at the checked slot offset represented by
276    /// `binding`, be readable for `'a`, and must not be reached through a
277    /// writable mapping in this process.
278    pub unsafe fn bind(
279        header: &'a SlotMetadata,
280        binding: ReaderSlotBinding,
281    ) -> Result<Self, SlotError> {
282        validate_bound_generation(header, binding.0.generation)?;
283        Ok(Self {
284            header,
285            binding: binding.0,
286        })
287    }
288
289    /// Acquires and validates publication metadata before an owned payload copy.
290    pub fn observe(&self, expected_sequence: u64) -> Result<SlotObservation, SlotError> {
291        validate_sequence_slot(self.binding, expected_sequence)?;
292        let sequence = self.header.published_sequence.load(Ordering::Acquire);
293        if sequence != expected_sequence {
294            return Err(SlotError::StaleSequence {
295                expected: expected_sequence,
296                actual: sequence,
297            });
298        }
299        validate_bound_generation(self.header, self.binding.generation)?;
300        let payload_len = self.header.payload_len.load(Ordering::Relaxed);
301        if payload_len > self.binding.payload_capacity {
302            return Err(SlotError::PayloadTooLarge {
303                length: payload_len,
304                capacity: self.binding.payload_capacity,
305            });
306        }
307        Ok(SlotObservation {
308            role: self.binding.role,
309            slot_index: self.binding.slot_index,
310            generation: self.binding.generation,
311            sequence,
312            payload_len,
313        })
314    }
315
316    /// Rechecks metadata stability after copying hostile bytes to owned storage.
317    ///
318    /// This detects metadata changes, not payload integrity. A malicious writer
319    /// can mutate bytes without changing metadata; callers must parse every
320    /// owned payload copy as hostile input.
321    pub fn recheck(&self, observation: SlotObservation) -> Result<(), SlotError> {
322        if observation.role != self.binding.role {
323            return Err(SlotError::WrongObservationRole);
324        }
325        if observation.generation != self.binding.generation {
326            return Err(SlotError::StaleGeneration {
327                expected: self.binding.generation,
328                actual: observation.generation,
329            });
330        }
331        // Keep preceding payload reads before the final metadata loads on
332        // weakly ordered hardware.
333        fence(Ordering::SeqCst);
334        let sequence = self.header.published_sequence.load(Ordering::Acquire);
335        if sequence != observation.sequence {
336            return Err(SlotError::StaleSequence {
337                expected: observation.sequence,
338                actual: sequence,
339            });
340        }
341        validate_bound_generation(self.header, observation.generation)?;
342        let payload_len = self.header.payload_len.load(Ordering::Relaxed);
343        if payload_len != observation.payload_len {
344            return Err(SlotError::ChangedPayloadLength {
345                expected: observation.payload_len,
346                actual: payload_len,
347            });
348        }
349        Ok(())
350    }
351}
352
353/// Permission to publish metadata after the caller has written the payload.
354#[must_use = "payload bytes remain unpublished until publish is called"]
355#[derive(Debug)]
356pub struct PublishReservation<'a> {
357    header: &'a SlotMetadata,
358    sequence: u64,
359    capacity: u32,
360    _exclusive: PhantomData<&'a mut ()>,
361}
362
363impl PublishReservation<'_> {
364    /// Stores length, then Release-publishes the nonzero sequence.
365    pub fn publish(self, payload_len: u32) -> Result<(), SlotError> {
366        if payload_len > self.capacity {
367            return Err(SlotError::PayloadTooLarge {
368                length: payload_len,
369                capacity: self.capacity,
370            });
371        }
372        self.header
373            .payload_len
374            .store(payload_len, Ordering::Relaxed);
375        self.header
376            .published_sequence
377            .store(self.sequence, Ordering::Release);
378        Ok(())
379    }
380}
381
382/// Owned identity observed before copying a slot payload.
383#[derive(Clone, Copy, Debug, Eq, PartialEq)]
384pub struct SlotObservation {
385    role: RoleId,
386    slot_index: u32,
387    generation: u64,
388    sequence: u64,
389    payload_len: u32,
390}
391
392impl SlotObservation {
393    /// Returns the producer region role.
394    pub const fn role(self) -> RoleId {
395        self.role
396    }
397    /// Returns the exact observed slot index.
398    pub const fn slot_index(self) -> u32 {
399        self.slot_index
400    }
401    /// Returns the connection generation.
402    pub const fn generation(self) -> u64 {
403        self.generation
404    }
405    /// Returns the published sequence.
406    pub const fn sequence(self) -> u64 {
407        self.sequence
408    }
409    /// Returns the validated payload length.
410    pub const fn payload_len(self) -> u32 {
411        self.payload_len
412    }
413}
414
415/// Immutable route metadata for a writable acknowledgement mapping.
416#[derive(Clone, Copy, Debug, Eq, PartialEq)]
417pub struct AcknowledgementWriterBinding(AcknowledgementBinding);
418
419impl AcknowledgementWriterBinding {
420    pub(crate) const fn validated(route: AcknowledgementRoute, generation: u64) -> Self {
421        Self(AcknowledgementBinding {
422            owner: route.owner(),
423            target: route.target(),
424            slot_index: route.slot_index(),
425            cell_index: route.cell_index(),
426            generation,
427        })
428    }
429}
430
431/// Immutable route metadata for a read-only acknowledgement mapping.
432#[derive(Clone, Copy, Debug, Eq, PartialEq)]
433pub struct AcknowledgementReaderBinding(AcknowledgementBinding);
434
435impl AcknowledgementReaderBinding {
436    pub(crate) const fn validated(route: AcknowledgementRoute, generation: u64) -> Self {
437        Self(AcknowledgementBinding {
438            owner: route.owner(),
439            target: route.target(),
440            slot_index: route.slot_index(),
441            cell_index: route.cell_index(),
442            generation,
443        })
444    }
445}
446
447#[derive(Clone, Copy, Debug, Eq, PartialEq)]
448struct AcknowledgementBinding {
449    owner: RoleId,
450    target: RoleId,
451    slot_index: u32,
452    cell_index: u32,
453    generation: u64,
454}
455
456/// Release-store capability for a cell in a writable mapping.
457pub struct AcknowledgementWriter<'a> {
458    cell: &'a AcknowledgementCell,
459    binding: AcknowledgementBinding,
460    _not_sync: PhantomData<Cell<()>>,
461}
462
463impl<'a> AcknowledgementWriter<'a> {
464    /// Binds a cell reached through its owner's writable mapping.
465    ///
466    /// # Safety
467    ///
468    /// `cell` must be the checked route cell described by `binding`; this
469    /// process must be the only writer of its containing mapping. No other
470    /// writer capability may be bound for this route while this value exists.
471    pub unsafe fn bind(
472        cell: &'a AcknowledgementCell,
473        binding: AcknowledgementWriterBinding,
474    ) -> Self {
475        Self {
476            cell,
477            binding: binding.0,
478            _not_sync: PhantomData,
479        }
480    }
481
482    /// Acknowledges an exact observed slot identity with Release ordering.
483    pub fn acknowledge(
484        &mut self,
485        observation: SlotObservation,
486    ) -> Result<(), AcknowledgementError> {
487        if observation.role != self.binding.target {
488            return Err(AcknowledgementError::WrongTarget);
489        }
490        if observation.generation != self.binding.generation {
491            return Err(AcknowledgementError::StaleGeneration);
492        }
493        if observation.slot_index != self.binding.slot_index {
494            return Err(AcknowledgementError::WrongSlot);
495        }
496        if observation.sequence == 0 {
497            return Err(AcknowledgementError::UnpublishedSequence);
498        }
499        let current = self.cell.sequence.load(Ordering::Relaxed);
500        if observation.sequence < current {
501            return Err(AcknowledgementError::NonMonotonic {
502                current,
503                next: observation.sequence,
504            });
505        }
506        if observation.sequence == current {
507            return Ok(());
508        }
509        self.cell
510            .sequence
511            .store(observation.sequence, Ordering::Release);
512        Ok(())
513    }
514}
515
516/// Acquire-only capability for a cell in a read-only mapping.
517pub struct AcknowledgementReader<'a> {
518    cell: &'a AcknowledgementCell,
519    binding: AcknowledgementBinding,
520}
521
522impl<'a> AcknowledgementReader<'a> {
523    /// Binds a cell reached through a read-only mapping.
524    ///
525    /// # Safety
526    ///
527    /// `cell` must be the checked route cell described by `binding` and must
528    /// remain readable for `'a`; this API must not be bound from a writable
529    /// alias in the current process.
530    pub unsafe fn bind(
531        cell: &'a AcknowledgementCell,
532        binding: AcknowledgementReaderBinding,
533    ) -> Self {
534        Self {
535            cell,
536            binding: binding.0,
537        }
538    }
539
540    /// Acquire-observes the exact route identity and current sequence.
541    pub fn observe(&self) -> AcknowledgementObservation {
542        AcknowledgementObservation {
543            owner: self.binding.owner,
544            target: self.binding.target,
545            generation: self.binding.generation,
546            slot_index: self.binding.slot_index,
547            cell_index: self.binding.cell_index,
548            sequence: self.cell.sequence.load(Ordering::Acquire),
549        }
550    }
551}
552
553/// Owned acknowledgement identity passed to a slot writer.
554#[derive(Clone, Copy, Debug, Eq, PartialEq)]
555pub struct AcknowledgementObservation {
556    owner: RoleId,
557    target: RoleId,
558    generation: u64,
559    slot_index: u32,
560    cell_index: u32,
561    sequence: u64,
562}
563
564impl AcknowledgementObservation {
565    /// Returns the role that owns the acknowledgement mapping.
566    pub const fn owner(self) -> RoleId {
567        self.owner
568    }
569    /// Returns the acknowledged producer role.
570    pub const fn target(self) -> RoleId {
571        self.target
572    }
573    /// Returns the acknowledged generation.
574    pub const fn generation(self) -> u64 {
575        self.generation
576    }
577    /// Returns the acknowledged producer slot.
578    pub const fn slot_index(self) -> u32 {
579        self.slot_index
580    }
581    /// Returns the exact acknowledgement cell.
582    pub const fn cell_index(self) -> u32 {
583        self.cell_index
584    }
585    /// Returns the acknowledged sequence.
586    pub const fn sequence(self) -> u64 {
587        self.sequence
588    }
589}
590
591/// Bounded slot validation failures.
592#[derive(Clone, Copy, Debug, Eq, PartialEq)]
593pub enum SlotError {
594    /// Generation zero is reserved.
595    ZeroGeneration,
596    /// Sequence zero is unpublished.
597    UnpublishedSequence,
598    /// Bound generation differs from shared metadata.
599    StaleGeneration {
600        /// Bound connection generation.
601        expected: u64,
602        /// Generation observed in shared metadata.
603        actual: u64,
604    },
605    /// Sequence would wrap.
606    SequenceWrap,
607    /// Sequence points at a different ring slot.
608    WrongSlot {
609        /// Slot selected by the publication sequence.
610        expected: u32,
611        /// Slot supplied by the caller or observation.
612        actual: u32,
613    },
614    /// First use of a slot had a noncanonical sequence.
615    UnexpectedFirstSequence {
616        /// Canonical first sequence for the slot.
617        expected: u64,
618        /// Requested first sequence.
619        actual: u64,
620    },
621    /// Reuse was not exactly one ring rotation later.
622    UnexpectedNextSequence {
623        /// Exact next sequence after one ring rotation.
624        expected: u64,
625        /// Requested reuse sequence.
626        actual: u64,
627    },
628    /// Reuse lacked an acknowledgement.
629    MissingAcknowledgement {
630        /// Prior sequence that must be acknowledged before reuse.
631        sequence: u64,
632    },
633    /// Acknowledgement targets another producer role.
634    WrongAcknowledgementTarget,
635    /// Acknowledgement came from another routed owner.
636    WrongAcknowledgementOwner,
637    /// Acknowledgement belongs to another producer slot.
638    WrongAcknowledgementSlot,
639    /// Acknowledgement came from another cell.
640    WrongAcknowledgementCell,
641    /// Acknowledgement came from another generation.
642    StaleAcknowledgementGeneration,
643    /// Acknowledgement lags the exact prior publication.
644    LaggingAcknowledgement {
645        /// Exact prior publication sequence required for reuse.
646        expected: u64,
647        /// Older acknowledged sequence supplied by the caller.
648        actual: u64,
649    },
650    /// Acknowledgement attempts to pre-authorize future reuse.
651    FutureAcknowledgement {
652        /// Exact prior publication sequence required for reuse.
653        expected: u64,
654        /// Future acknowledged sequence supplied by the caller.
655        actual: u64,
656    },
657    /// Observed sequence differs from expectation.
658    StaleSequence {
659        /// Sequence requested by the reader.
660        expected: u64,
661        /// Sequence observed in shared metadata.
662        actual: u64,
663    },
664    /// Peer-declared payload exceeds fixed capacity.
665    PayloadTooLarge {
666        /// Peer-declared payload length.
667        length: u32,
668        /// Validated fixed payload capacity.
669        capacity: u32,
670    },
671    /// Payload length changed during the copy window.
672    ChangedPayloadLength {
673        /// Payload length captured before the copy.
674        expected: u32,
675        /// Payload length observed during the recheck.
676        actual: u32,
677    },
678    /// Observation belongs to another role.
679    WrongObservationRole,
680}
681
682/// Bounded acknowledgement failures.
683#[derive(Clone, Copy, Debug, Eq, PartialEq)]
684pub enum AcknowledgementError {
685    /// Slot observation targets another route.
686    WrongTarget,
687    /// Slot observation belongs to another generation.
688    StaleGeneration,
689    /// Slot observation belongs to another routed slot.
690    WrongSlot,
691    /// Sequence zero is unpublished.
692    UnpublishedSequence,
693    /// Acknowledgement moved backwards.
694    NonMonotonic {
695        /// Currently published acknowledgement sequence.
696        current: u64,
697        /// Requested acknowledgement sequence.
698        next: u64,
699    },
700}
701
702impl fmt::Display for SlotError {
703    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
704        write!(formatter, "slot operation failed: {self:?}")
705    }
706}
707impl fmt::Display for AcknowledgementError {
708    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
709        write!(formatter, "acknowledgement failed: {self:?}")
710    }
711}
712
713#[cfg(feature = "std")]
714impl std::error::Error for SlotError {}
715#[cfg(feature = "std")]
716impl std::error::Error for AcknowledgementError {}
717
718fn validate_bound_generation(header: &SlotMetadata, expected: u64) -> Result<(), SlotError> {
719    if expected == 0 {
720        return Err(SlotError::ZeroGeneration);
721    }
722    let actual = header.generation.load(Ordering::Relaxed);
723    if actual == expected {
724        Ok(())
725    } else {
726        Err(SlotError::StaleGeneration { expected, actual })
727    }
728}
729
730fn validate_sequence_slot(binding: SlotBinding, sequence: u64) -> Result<(), SlotError> {
731    if sequence == 0 {
732        return Err(SlotError::UnpublishedSequence);
733    }
734    let expected = ((sequence - 1) % u64::from(binding.slot_count)) as u32;
735    if binding.slot_index == expected {
736        Ok(())
737    } else {
738        Err(SlotError::WrongSlot {
739            expected,
740            actual: binding.slot_index,
741        })
742    }
743}
744
745const _: () = assert!(core::mem::size_of::<SlotMetadata>() == SLOT_HEADER_SIZE as usize);
746const _: () = assert!(core::mem::align_of::<SlotMetadata>() == 64);
747const _: () = assert!(core::mem::offset_of!(SlotMetadata, generation) == 0);
748const _: () = assert!(core::mem::offset_of!(SlotMetadata, payload_len) == 8);
749const _: () = assert!(core::mem::offset_of!(SlotMetadata, reserved_word) == 12);
750const _: () = assert!(core::mem::offset_of!(SlotMetadata, published_sequence) == 16);
751const _: () = assert!(core::mem::offset_of!(SlotMetadata, reserved) == 24);
752const _: () = assert!(core::mem::size_of::<AcknowledgementCell>() == 64);
753const _: () = assert!(core::mem::align_of::<AcknowledgementCell>() == 64);
754const _: () = assert!(core::mem::offset_of!(AcknowledgementCell, sequence) == 0);
755const _: () = assert!(core::mem::offset_of!(AcknowledgementCell, reserved) == 8);
756
757#[cfg(test)]
758#[path = "slot_test.rs"]
759mod tests;