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            unsafe {
33                self.ptr.as_ptr().drop_in_place();
34            }
35        }
36    }
37}
38
39unsafe impl<T: Wire> Wire for Box<'static, T> {
40    type Narrowed<'de> = Box<'de, T::Narrowed<'de>>;
41
42    #[inline]
43    fn zero_padding(_: &mut MaybeUninit<Self>) {
44        // Wire boxes have no padding
45    }
46}
47
48impl<T> Box<'_, T> {
49    /// Encodes that a value is present in an output.
50    pub fn encode_present(out: &mut MaybeUninit<Self>) {
51        munge!(let Self { ptr } = out);
52        wire::Pointer::encode_present(ptr);
53    }
54
55    /// Encodes that a value is absent in a slot.
56    pub fn encode_absent(out: &mut MaybeUninit<Self>) {
57        munge!(let Self { ptr } = out);
58        wire::Pointer::encode_absent(ptr);
59    }
60
61    /// Returns whether the value is present.
62    pub fn is_some(&self) -> bool {
63        !self.ptr.as_ptr().is_null()
64    }
65
66    /// Returns whether the value is absent.
67    pub fn is_none(&self) -> bool {
68        !self.is_some()
69    }
70
71    /// Returns a reference to the boxed value, if any.
72    pub fn as_ref(&self) -> Option<&T> {
73        NonNull::new(self.ptr.as_ptr()).map(|ptr| unsafe { ptr.as_ref() })
74    }
75
76    /// Returns an `Owned` of the boxed value, if any.
77    pub fn into_option(self) -> Option<T> {
78        let ptr = self.ptr.as_ptr();
79        forget(self);
80        if ptr.is_null() { None } else { unsafe { Some(ptr.read()) } }
81    }
82}
83
84impl<T: fmt::Debug> fmt::Debug for Box<'_, T> {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        self.as_ref().fmt(f)
87    }
88}
89
90unsafe impl<'de, D: Decoder<'de> + ?Sized, T: Decode<D>> Decode<D> for Box<'de, T> {
91    fn decode(
92        slot: Slot<'_, Self>,
93        decoder: &mut D,
94        constraint: Self::Constraint,
95    ) -> Result<(), DecodeError> {
96        munge!(let Self { mut ptr } = slot);
97
98        if wire::Pointer::is_encoded_present(ptr.as_mut())? {
99            let mut value = decoder.take_slot::<T>()?;
100            T::decode(value.as_mut(), decoder, constraint)?;
101            wire::Pointer::set_decoded(ptr, value);
102        }
103
104        Ok(())
105    }
106}
107
108impl<T: FromWire<W>, W> FromWireOption<Box<'_, W>> for T {
109    fn from_wire_option(wire: Box<'_, W>) -> Option<Self> {
110        wire.into_option().map(T::from_wire)
111    }
112}
113
114impl<T: IntoNatural> IntoNatural for Box<'_, T> {
115    type Natural = Option<T::Natural>;
116}
117
118impl<T: FromWireRef<W>, W> FromWireOptionRef<Box<'_, W>> for T {
119    fn from_wire_option_ref(wire: &Box<'_, W>) -> Option<Self> {
120        wire.as_ref().map(T::from_wire_ref)
121    }
122}
123
124impl<T: Constrained> Constrained for Box<'_, T> {
125    type Constraint = T::Constraint;
126
127    fn validate(slot: Slot<'_, Self>, constraint: Self::Constraint) -> Result<(), ValidationError> {
128        munge!(let Self { ptr } = slot);
129
130        let ptr = unsafe { ptr.deref_unchecked() };
131        let ptr = ptr.as_ptr();
132        let member_slot = unsafe { Slot::new_unchecked(ptr) };
133        T::validate(member_slot, constraint)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use crate::{DecoderExt as _, EncoderExt as _, chunks, wire};
140
141    #[test]
142    fn decode_box() {
143        assert_eq!(
144            chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
145                .as_mut_slice()
146                .decode::<wire::Box<'_, wire::Uint64>>()
147                .unwrap()
148                .as_ref(),
149            None,
150        );
151        assert_eq!(
152            chunks![
153                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
154                0x34, 0x12,
155            ]
156            .as_mut_slice()
157            .decode::<wire::Box<'_, wire::Uint64>>()
158            .unwrap()
159            .as_ref(),
160            Some(&wire::Uint64(0x123456789abcdef0u64)),
161        );
162    }
163
164    #[test]
165    fn encode_box() {
166        assert_eq!(
167            Vec::encode(None::<u64>).unwrap(),
168            chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
169        );
170        assert_eq!(
171            Vec::encode(Some(0x123456789abcdef0u64)).unwrap(),
172            chunks![
173                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
174                0x34, 0x12,
175            ],
176        );
177    }
178}