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}