fidl_next_codec/fuchsia/
handle.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::cell::Cell;
6use core::fmt;
7use core::mem::{ManuallyDrop, MaybeUninit};
8
9use zx::sys::{zx_handle_t, ZX_HANDLE_INVALID};
10use zx::Handle;
11
12use crate::fuchsia::{HandleDecoder, HandleEncoder};
13use crate::{
14    munge, Decode, DecodeError, Encodable, EncodableOption, Encode, EncodeError, EncodeOption,
15    Slot, TakeFrom, WireU32, ZeroPadding,
16};
17
18/// A Zircon handle.
19#[repr(C, align(4))]
20pub union WireHandle {
21    encoded: WireU32,
22    decoded: ManuallyDrop<Cell<zx_handle_t>>,
23}
24
25impl Drop for WireHandle {
26    fn drop(&mut self) {
27        drop(self.take());
28    }
29}
30
31unsafe impl ZeroPadding for WireHandle {
32    #[inline]
33    fn zero_padding(_: &mut MaybeUninit<Self>) {
34        // Wire handles have no padding
35    }
36}
37
38impl WireHandle {
39    /// Encodes a handle as present in an output.
40    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
41        munge!(let Self { encoded } = out);
42        encoded.write(WireU32(u32::MAX));
43    }
44
45    /// Returns whether the underlying `zx_handle_t` is invalid.
46    pub fn is_invalid(&self) -> bool {
47        self.as_raw_handle() == ZX_HANDLE_INVALID
48    }
49
50    /// Takes the handle, if any, leaving an invalid handle in its place.
51    pub fn take(&self) -> Handle {
52        let raw = unsafe { self.decoded.replace(ZX_HANDLE_INVALID) };
53        unsafe { Handle::from_raw(raw) }
54    }
55
56    /// Returns the underlying [`zx_handle_t`].
57    #[inline]
58    pub fn as_raw_handle(&self) -> zx_handle_t {
59        unsafe { self.decoded.get() }
60    }
61}
62
63impl fmt::Debug for WireHandle {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        self.as_raw_handle().fmt(f)
66    }
67}
68
69unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for WireHandle {
70    fn decode(mut slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
71        munge!(let Self { encoded } = slot.as_mut());
72
73        match **encoded {
74            0 => (),
75            u32::MAX => {
76                let handle = decoder.take_raw_handle()?;
77                munge!(let Self { mut decoded } = slot);
78                // SAFETY: `Cell` has no uninit bytes, even though it doesn't implement `IntoBytes`.
79                unsafe {
80                    decoded.as_mut_ptr().write(ManuallyDrop::new(Cell::new(handle)));
81                }
82            }
83            e => return Err(DecodeError::InvalidHandlePresence(e)),
84        }
85        Ok(())
86    }
87}
88
89impl TakeFrom<WireHandle> for Handle {
90    fn take_from(from: &WireHandle) -> Self {
91        from.take()
92    }
93}
94
95/// An optional Zircon handle.
96#[derive(Debug)]
97#[repr(transparent)]
98pub struct WireOptionalHandle {
99    handle: WireHandle,
100}
101
102unsafe impl ZeroPadding for WireOptionalHandle {
103    #[inline]
104    fn zero_padding(out: &mut MaybeUninit<Self>) {
105        munge!(let Self { handle } = out);
106        WireHandle::zero_padding(handle);
107    }
108}
109
110impl WireOptionalHandle {
111    /// Encodes a handle as present in a slot.
112    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
113        munge!(let Self { handle } = out);
114        WireHandle::set_encoded_present(handle);
115    }
116
117    /// Encodes a handle as absent in an output.
118    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
119        munge!(let Self { handle: WireHandle { encoded } } = out);
120        encoded.write(WireU32(ZX_HANDLE_INVALID));
121    }
122
123    /// Returns whether a handle is present.
124    pub fn is_some(&self) -> bool {
125        !self.handle.is_invalid()
126    }
127
128    /// Returns whether a handle is absent.
129    pub fn is_none(&self) -> bool {
130        self.handle.is_invalid()
131    }
132
133    /// Takes the handle, if any, leaving an invalid handle in its place.
134    pub fn take(&self) -> Option<Handle> {
135        self.is_some().then(|| self.handle.take())
136    }
137
138    /// Returns the underlying [`zx_handle_t`], if any.
139    #[inline]
140    pub fn as_raw_handle(&self) -> Option<zx_handle_t> {
141        self.is_some().then(|| self.handle.as_raw_handle())
142    }
143}
144
145impl Encodable for Handle {
146    type Encoded = WireHandle;
147}
148
149unsafe impl<E: HandleEncoder + ?Sized> Encode<E> for Handle {
150    fn encode(
151        self,
152        encoder: &mut E,
153        out: &mut MaybeUninit<Self::Encoded>,
154    ) -> Result<(), EncodeError> {
155        if self.is_invalid() {
156            Err(EncodeError::InvalidRequiredHandle)
157        } else {
158            encoder.push_handle(self)?;
159            WireHandle::set_encoded_present(out);
160            Ok(())
161        }
162    }
163}
164
165impl EncodableOption for Handle {
166    type EncodedOption = WireOptionalHandle;
167}
168
169unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<E> for Handle {
170    fn encode_option(
171        this: Option<Self>,
172        encoder: &mut E,
173        out: &mut MaybeUninit<Self::EncodedOption>,
174    ) -> Result<(), EncodeError> {
175        if let Some(handle) = this {
176            encoder.push_handle(handle)?;
177            WireOptionalHandle::set_encoded_present(out);
178        } else {
179            WireOptionalHandle::set_encoded_absent(out);
180        }
181        Ok(())
182    }
183}
184
185unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for WireOptionalHandle {
186    fn decode(mut slot: Slot<'_, Self>, decoder: &mut D) -> Result<(), DecodeError> {
187        munge!(let Self { handle } = slot.as_mut());
188        WireHandle::decode(handle, decoder)
189    }
190}
191
192impl TakeFrom<WireOptionalHandle> for Option<Handle> {
193    fn take_from(from: &WireOptionalHandle) -> Self {
194        from.take()
195    }
196}
197
198// TODO: newtype handle subtypes
199macro_rules! impl_takefrom {
200    ($($name:ident),* $(,)?) => {
201        $(
202            impl TakeFrom<WireHandle> for zx::$name {
203                fn take_from(from: &WireHandle) -> zx::$name {
204                    from.take().into()
205                }
206            }
207
208            impl TakeFrom<WireOptionalHandle> for Option<zx::$name> {
209                fn take_from(from: &WireOptionalHandle) -> Self {
210                    from.take().map(<zx::$name>::from)
211                }
212            }
213        )*
214    }
215}
216
217impl_takefrom! {
218    Process,
219    Thread,
220    Vmo,
221    Channel,
222    Event,
223    Port,
224    Interrupt,
225    Socket,
226    Resource,
227    EventPair,
228    Job,
229    Vmar,
230    Fifo,
231    Guest,
232    Vcpu,
233    Timer,
234    Iommu,
235    Bti,
236    Profile,
237    Pmt,
238    Pager,
239    Exception,
240    Clock,
241    Stream,
242    Iob,
243}