Skip to main content

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 _, FromWire, FromWireOption,
13    FromWireOptionRef, FromWireRef, IntoNatural, Slot, ValidationError, Wire, wire,
14};
15
16/// A boxed (optional) FIDL value.
17#[repr(C)]
18pub struct Box<'de, T> {
19    ptr: wire::Pointer<'de, T>,
20}
21
22// SAFETY: `WireBox` doesn't add any restrictions on sending across thread boundaries, and so is
23// `Send` if `T` is `Send`.
24unsafe impl<T: Send> Send for Box<'_, T> {}
25
26// SAFETY: `WireBox` doesn't add any interior mutability, so it is `Sync` if `T` is `Sync`.
27unsafe impl<T: Sync> Sync for Box<'_, T> {}
28
29impl<T> Drop for Box<'_, T> {
30    fn drop(&mut self) {
31        if self.is_some() {
32            // SAFETY: The pointer is not null (checked by `is_some`), and points to a valid
33            // allocated `T` owned by the `Box`.
34            unsafe {
35                self.ptr.as_ptr().drop_in_place();
36            }
37        }
38    }
39}
40
41// SAFETY: `Box` has the same layout as `Pointer`, which is a valid `Wire` type.
42unsafe impl<T: Wire> Wire for Box<'static, T> {
43    type Narrowed<'de> = Box<'de, T::Narrowed<'de>>;
44
45    #[inline]
46    fn zero_padding(_: &mut MaybeUninit<Self>) {
47        // Wire boxes have no padding
48    }
49}
50
51impl<T> Box<'_, T> {
52    /// Encodes that a value is present in an output.
53    pub fn encode_present(out: &mut MaybeUninit<Self>) {
54        munge!(let Self { ptr } = out);
55        wire::Pointer::encode_present(ptr);
56    }
57
58    /// Encodes that a value is absent in a slot.
59    pub fn encode_absent(out: &mut MaybeUninit<Self>) {
60        munge!(let Self { ptr } = out);
61        wire::Pointer::encode_absent(ptr);
62    }
63
64    /// Returns whether the value is present.
65    pub fn is_some(&self) -> bool {
66        !self.ptr.as_ptr().is_null()
67    }
68
69    /// Returns whether the value is absent.
70    pub fn is_none(&self) -> bool {
71        !self.is_some()
72    }
73
74    /// Returns a reference to the boxed value, if any.
75    pub fn as_ref(&self) -> Option<&T> {
76        // SAFETY: `ptr` is guaranteed to be valid and aligned if it is not null.
77        NonNull::new(self.ptr.as_ptr()).map(|ptr| unsafe { ptr.as_ref() })
78    }
79
80    /// Returns an `Owned` of the boxed value, if any.
81    pub fn into_option(self) -> Option<T> {
82        let ptr = self.ptr.as_ptr();
83        forget(self);
84        if ptr.is_null() {
85            None
86        } else {
87            // SAFETY: `ptr` is not null, and we have consumed `self` (ownership of the
88            // pointed-to value is transferred to us).
89            unsafe { Some(ptr.read()) }
90        }
91    }
92}
93
94impl<T: fmt::Debug> fmt::Debug for Box<'_, T> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        self.as_ref().fmt(f)
97    }
98}
99
100// SAFETY: `Box` has the same layout as `Pointer`, and decoding it as a `Pointer` is safe.
101unsafe impl<'de, D: Decoder<'de> + ?Sized, T: Decode<D>> Decode<D> for Box<'de, T> {
102    fn decode(
103        slot: Slot<'_, Self>,
104        decoder: &mut D,
105        constraint: Self::Constraint,
106    ) -> Result<(), DecodeError> {
107        munge!(let Self { mut ptr } = slot);
108
109        if wire::Pointer::is_encoded_present(ptr.as_mut())? {
110            let mut value = decoder.take_slot::<T>()?;
111            T::decode(value.as_mut(), decoder, constraint)?;
112            wire::Pointer::set_decoded(ptr, value);
113        }
114
115        Ok(())
116    }
117}
118
119impl<T: FromWire<W>, W> FromWireOption<Box<'_, W>> for T {
120    fn from_wire_option(wire: Box<'_, W>) -> Option<Self> {
121        wire.into_option().map(T::from_wire)
122    }
123}
124
125impl<T: IntoNatural> IntoNatural for Box<'_, T> {
126    type Natural = Option<T::Natural>;
127}
128
129impl<T: FromWireRef<W>, W> FromWireOptionRef<Box<'_, W>> for T {
130    fn from_wire_option_ref(wire: &Box<'_, W>) -> Option<Self> {
131        wire.as_ref().map(T::from_wire_ref)
132    }
133}
134
135impl<T: Constrained> Constrained for Box<'_, T> {
136    type Constraint = T::Constraint;
137
138    fn validate(slot: Slot<'_, Self>, constraint: Self::Constraint) -> Result<(), ValidationError> {
139        munge!(let Self { ptr } = slot);
140
141        // SAFETY: `ptr` is a slot for a `Pointer`, which is a union. The validator
142        // guarantees that the slot contains initialized bytes, so dereferencing it is safe.
143        let ptr = unsafe { ptr.deref_unchecked() };
144        let ptr = ptr.as_ptr();
145        // SAFETY: `ptr` is the decoded pointer. If the box is present, it points to a
146        // valid initialized `T`. If absent, it is null. `Slot::new_unchecked` is safe
147        // to call with null as long as the resulting slot is not dereferenced.
148        let member_slot = unsafe { Slot::new_unchecked(ptr) };
149        T::validate(member_slot, constraint)
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use crate::{DecoderExt as _, EncoderExt as _, chunks, wire};
156
157    #[test]
158    fn decode_box() {
159        assert_eq!(
160            chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
161                .as_mut_slice()
162                .decode::<wire::Box<'_, wire::Uint64>>()
163                .unwrap()
164                .as_ref(),
165            None,
166        );
167        assert_eq!(
168            chunks![
169                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
170                0x34, 0x12,
171            ]
172            .as_mut_slice()
173            .decode::<wire::Box<'_, wire::Uint64>>()
174            .unwrap()
175            .as_ref(),
176            Some(&wire::Uint64(0x123456789abcdef0u64)),
177        );
178    }
179
180    #[test]
181    fn encode_box() {
182        assert_eq!(
183            Vec::encode(None::<u64>).unwrap(),
184            chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
185        );
186        assert_eq!(
187            Vec::encode(Some(0x123456789abcdef0u64)).unwrap(),
188            chunks![
189                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
190                0x34, 0x12,
191            ],
192        );
193    }
194}