fidl_next_codec/wire/
boxed.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::fmt;
6use core::mem::{MaybeUninit, forget};
7use core::ptr::NonNull;
8
9use munge::munge;
10
11use crate::{
12    Constrained, Decode, DecodeError, Decoder, DecoderExt as _, Encodable, EncodableOption, Encode,
13    EncodeError, EncodeOption, EncodeOptionRef, EncodeRef, FromWire, FromWireOption,
14    FromWireOptionRef, FromWireRef, IntoNatural, Slot, ValidationError, Wire, WirePointer,
15};
16
17/// A boxed (optional) FIDL value.
18#[repr(C)]
19pub struct WireBox<'de, T> {
20    ptr: WirePointer<'de, T>,
21}
22
23// SAFETY: `WireBox` doesn't add any restrictions on sending across thread boundaries, and so is
24// `Send` if `T` is `Send`.
25unsafe impl<T: Send> Send for WireBox<'_, T> {}
26
27// SAFETY: `WireBox` doesn't add any interior mutability, so it is `Sync` if `T` is `Sync`.
28unsafe impl<T: Sync> Sync for WireBox<'_, T> {}
29
30impl<T> Drop for WireBox<'_, T> {
31    fn drop(&mut self) {
32        if self.is_some() {
33            unsafe {
34                self.ptr.as_ptr().drop_in_place();
35            }
36        }
37    }
38}
39
40unsafe impl<T: Wire> Wire for WireBox<'static, T> {
41    type Decoded<'de> = WireBox<'de, T::Decoded<'de>>;
42
43    #[inline]
44    fn zero_padding(_: &mut MaybeUninit<Self>) {
45        // Wire boxes have no padding
46    }
47}
48
49impl<T> WireBox<'_, T> {
50    /// Encodes that a value is present in an output.
51    pub fn encode_present(out: &mut MaybeUninit<Self>) {
52        munge!(let Self { ptr } = out);
53        WirePointer::encode_present(ptr);
54    }
55
56    /// Encodes that a value is absent in a slot.
57    pub fn encode_absent(out: &mut MaybeUninit<Self>) {
58        munge!(let Self { ptr } = out);
59        WirePointer::encode_absent(ptr);
60    }
61
62    /// Returns whether the value is present.
63    pub fn is_some(&self) -> bool {
64        !self.ptr.as_ptr().is_null()
65    }
66
67    /// Returns whether the value is absent.
68    pub fn is_none(&self) -> bool {
69        !self.is_some()
70    }
71
72    /// Returns a reference to the boxed value, if any.
73    pub fn as_ref(&self) -> Option<&T> {
74        NonNull::new(self.ptr.as_ptr()).map(|ptr| unsafe { ptr.as_ref() })
75    }
76
77    /// Returns an `Owned` of the boxed value, if any.
78    pub fn into_option(self) -> Option<T> {
79        let ptr = self.ptr.as_ptr();
80        forget(self);
81        if ptr.is_null() { None } else { unsafe { Some(ptr.read()) } }
82    }
83}
84
85impl<T: fmt::Debug> fmt::Debug for WireBox<'_, T> {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        self.as_ref().fmt(f)
88    }
89}
90
91unsafe impl<D: Decoder + ?Sized, T: Decode<D>> Decode<D> for WireBox<'static, T> {
92    fn decode(
93        slot: Slot<'_, Self>,
94        mut decoder: &mut D,
95        constraint: <Self as Constrained>::Constraint,
96    ) -> Result<(), DecodeError> {
97        munge!(let Self { mut ptr } = slot);
98
99        if WirePointer::is_encoded_present(ptr.as_mut())? {
100            let mut value = decoder.take_slot::<T>()?;
101            T::decode(value.as_mut(), decoder, constraint)?;
102            WirePointer::set_decoded(ptr, value.as_mut_ptr());
103        }
104
105        Ok(())
106    }
107}
108
109impl<T: EncodableOption> Encodable for Option<T> {
110    type Encoded = T::EncodedOption;
111}
112
113unsafe impl<E: ?Sized, T: EncodeOption<E>> Encode<E> for Option<T> {
114    fn encode(
115        self,
116        encoder: &mut E,
117        out: &mut MaybeUninit<Self::Encoded>,
118        constraint: <Self::Encoded as Constrained>::Constraint,
119    ) -> Result<(), EncodeError> {
120        T::encode_option(self, encoder, out, constraint)
121    }
122}
123
124unsafe impl<E: ?Sized, T: EncodeOptionRef<E>> EncodeRef<E> for Option<T> {
125    fn encode_ref(
126        &self,
127        encoder: &mut E,
128        out: &mut MaybeUninit<Self::Encoded>,
129        constraint: <Self::Encoded as Constrained>::Constraint,
130    ) -> Result<(), EncodeError> {
131        T::encode_option_ref(self.as_ref(), encoder, out, constraint)
132    }
133}
134
135impl<T: FromWire<W>, W> FromWireOption<WireBox<'_, W>> for T {
136    fn from_wire_option(wire: WireBox<'_, W>) -> Option<Self> {
137        wire.into_option().map(T::from_wire)
138    }
139}
140
141impl<T: IntoNatural> IntoNatural for WireBox<'_, T> {
142    type Natural = Option<T::Natural>;
143}
144
145impl<T: FromWireRef<W>, W> FromWireOptionRef<WireBox<'_, W>> for T {
146    fn from_wire_option_ref(wire: &WireBox<'_, W>) -> Option<Self> {
147        wire.as_ref().map(T::from_wire_ref)
148    }
149}
150
151impl<T: Constrained> Constrained for WireBox<'_, T> {
152    type Constraint = T::Constraint;
153
154    fn validate(slot: Slot<'_, Self>, constraint: Self::Constraint) -> Result<(), ValidationError> {
155        munge!(let Self { ptr } = slot);
156
157        let ptr = unsafe { ptr.deref_unchecked() };
158        let ptr = ptr.as_ptr();
159        let member_slot = unsafe { Slot::new_unchecked(ptr) };
160        T::validate(member_slot, constraint)
161    }
162}