Skip to main content

fidl_next_codec/
slot.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::marker::PhantomData;
6use core::mem::MaybeUninit;
7use core::ops::{Deref, DerefMut};
8use core::ptr::slice_from_raw_parts_mut;
9use core::slice::from_raw_parts;
10
11use munge::{Destructure, Move, Restructure};
12use zerocopy::{FromBytes, IntoBytes};
13
14/// An initialized but potentially invalid value.
15///
16/// The bytes of a `Slot` are always valid to read, but may not represent a
17/// valid value of its type. For example, a `Slot<'_, bool>` may not be set to
18/// 0 or 1.
19#[repr(transparent)]
20pub struct Slot<'de, T: ?Sized> {
21    ptr: *mut T,
22    _phantom: PhantomData<&'de mut [u8]>,
23}
24
25// SAFETY: `Slot` represents exclusive ownership of a `T`, so it is `Send` if `T` is `Send`.
26unsafe impl<T: Send> Send for Slot<'_, T> {}
27// SAFETY: `Slot` represents exclusive ownership of a `T`, so it is `Sync` if `T` is `Sync`.
28unsafe impl<T: Sync> Sync for Slot<'_, T> {}
29
30impl<'de, T: ?Sized> Slot<'de, T> {
31    /// Returns a new `Slot` backed by the given `MaybeUninit`.
32    pub fn new(backing: &'de mut MaybeUninit<T>) -> Self
33    where
34        T: Sized,
35    {
36        // SAFETY: `backing` is a valid mutable reference, so its pointer is valid for writes.
37        unsafe {
38            backing.as_mut_ptr().write_bytes(0, 1);
39        }
40        // SAFETY: The memory has been initialized to zero by the previous `write_bytes` call.
41        unsafe { Self::new_unchecked(backing.as_mut_ptr()) }
42    }
43
44    /// Creates a new slot from the given pointer.
45    ///
46    /// # Safety
47    ///
48    /// `ptr` must point to enough initialized bytes with the correct alignment
49    /// to represent a `T`.
50    pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
51        Self { ptr, _phantom: PhantomData }
52    }
53
54    /// Mutably reborrows the slot.
55    pub fn as_mut(&mut self) -> Slot<'_, T> {
56        Self { ptr: self.ptr, _phantom: PhantomData }
57    }
58
59    /// Returns a mutable pointer to the underlying potentially-invalid value.
60    pub fn as_mut_ptr(&mut self) -> *mut T {
61        self.ptr
62    }
63
64    /// Returns a pointer to the underlying potentially-invalid value.
65    pub fn as_ptr(&self) -> *const T {
66        self.ptr
67    }
68
69    /// Returns a reference to the contained value.
70    ///
71    /// # Safety
72    ///
73    /// The slot must contain a valid `T`.
74    pub unsafe fn deref_unchecked(&self) -> &T {
75        // SAFETY: The caller guarantees that the slot contains a valid `T`.
76        unsafe { &*self.as_ptr() }
77    }
78
79    /// Returns a mutable reference to the contained value.
80    ///
81    /// # Safety
82    ///
83    /// The slot must contain a valid `T`.
84    pub unsafe fn deref_mut_unchecked(&mut self) -> &mut T {
85        // SAFETY: The caller guarantees that the slot contains a valid `T`.
86        unsafe { &mut *self.as_mut_ptr() }
87    }
88
89    /// Writes the given value into the slot.
90    pub fn write(&mut self, value: T)
91    where
92        T: IntoBytes + Sized,
93    {
94        // SAFETY: `self.ptr` is valid for writes.
95        unsafe {
96            self.as_mut_ptr().write(value);
97        }
98    }
99}
100
101impl<'de, T: Sized> Slot<'de, T> {
102    /// Creates a new slot from the given backing storage.
103    ///
104    /// # Safety
105    ///
106    /// `backing` must actually be initialized with valid `T`.
107    pub unsafe fn new_unchecked_from_maybe_uninit(backing: &mut MaybeUninit<T>) -> Self {
108        Self { ptr: backing.as_mut_ptr(), _phantom: PhantomData }
109    }
110}
111
112impl<T> Slot<'_, T> {
113    /// Returns a slice of the underlying bytes.
114    pub fn as_bytes(&self) -> &[u8] {
115        // SAFETY: `self.ptr` is valid for reads of `size_of::<T>()` bytes because it
116        // points to initialized memory.
117        unsafe { from_raw_parts(self.ptr.cast::<u8>(), size_of::<T>()) }
118    }
119}
120
121impl<T, const N: usize> Slot<'_, [T; N]> {
122    /// Returns a slot of the element at the given index.
123    pub fn index(&mut self, index: usize) -> Slot<'_, T> {
124        assert!(index < N, "attempted to index out-of-bounds");
125
126        // SAFETY: `index` is checked to be within bounds, so the calculated pointer is valid.
127        Slot { ptr: unsafe { self.as_mut_ptr().cast::<T>().add(index) }, _phantom: PhantomData }
128    }
129}
130
131impl<T> Slot<'_, [T]> {
132    /// Creates a new slice slot from the given pointer.
133    ///
134    /// # Safety
135    ///
136    /// `ptr` must point to enough initialized bytes with the correct alignment
137    /// to represent a slice of `len` `T`s.
138    pub unsafe fn new_slice_unchecked(ptr: *mut T, len: usize) -> Self {
139        Self { ptr: slice_from_raw_parts_mut(ptr, len), _phantom: PhantomData }
140    }
141
142    /// Returns a slot of the element at the given index.
143    pub fn index(&mut self, index: usize) -> Slot<'_, T> {
144        assert!(index < self.ptr.len(), "attempted to index out-of-bounds");
145
146        // SAFETY: `index` is checked to be within bounds of the slice, so the calculated pointer
147        // is valid.
148        Slot { ptr: unsafe { self.as_mut_ptr().cast::<T>().add(index) }, _phantom: PhantomData }
149    }
150}
151
152impl<T: FromBytes> Deref for Slot<'_, T> {
153    type Target = T;
154
155    fn deref(&self) -> &Self::Target {
156        // SAFETY: `T` implements `FromBytes`, so any initialized byte sequence represents a valid
157        // `T`.
158        unsafe { &*self.as_ptr() }
159    }
160}
161
162impl<T: FromBytes> DerefMut for Slot<'_, T> {
163    fn deref_mut(&mut self) -> &mut Self::Target {
164        // SAFETY: `T` implements `FromBytes`, so any initialized byte sequence represents a valid
165        // `T`.
166        unsafe { &mut *self.as_mut_ptr() }
167    }
168}
169
170impl<'de, T> Iterator for Slot<'de, [T]> {
171    type Item = Slot<'de, T>;
172
173    fn next(&mut self) -> Option<Self::Item> {
174        if self.ptr.len() == 0 {
175            return None;
176        }
177
178        let result = Slot { ptr: self.ptr.cast::<T>(), _phantom: PhantomData };
179
180        self.ptr =
181            // SAFETY: The slice has at least one element, so advancing the pointer by 1 is safe.
182            slice_from_raw_parts_mut(unsafe { self.ptr.cast::<T>().add(1) }, self.ptr.len() - 1);
183
184        Some(result)
185    }
186}
187
188// SAFETY: `underlying` returns a pointer to the wrapped data, which is valid and uniquely owned by
189// the `Slot`.
190unsafe impl<T> Destructure for Slot<'_, T> {
191    type Underlying = T;
192    type Destructuring = Move;
193
194    fn underlying(&mut self) -> *mut Self::Underlying {
195        self.as_mut_ptr()
196    }
197}
198
199// SAFETY: `restructure` is called with a pointer to a subfield of `T` which is guaranteed to be
200// initialized and have the correct lifetime.
201unsafe impl<'de, T, U: 'de> Restructure<U> for Slot<'de, T> {
202    type Restructured = Slot<'de, U>;
203
204    unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured {
205        Slot { ptr, _phantom: PhantomData }
206    }
207}