fidl_next_codec/wire/ptr.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;
7
8use munge::munge;
9
10use crate::{Chunk, DecodeError, Slot, wire};
11use fidl_constants::{ALLOC_ABSENT_U64, ALLOC_PRESENT_U64};
12
13/// A raw FIDL pointer
14#[repr(C, align(8))]
15pub union Pointer<'de, T> {
16 encoded: wire::Uint64,
17 decoded: *mut T,
18 _phantom: PhantomData<&'de mut [Chunk]>,
19}
20
21// SAFETY: `Pointer` is a raw pointer wrapper and doesn't add any thread safety restrictions.
22unsafe impl<T: Send> Send for Pointer<'_, T> {}
23// SAFETY: `Pointer` contains no interior mutability.
24unsafe impl<T: Sync> Sync for Pointer<'_, T> {}
25
26impl<'de, T> Pointer<'de, T> {
27 /// Returns whether the wire pointer was encoded present.
28 pub fn is_encoded_present(slot: Slot<'_, Self>) -> Result<bool, DecodeError> {
29 // `unsafe` block required in the next version of munge
30 #[allow(unused_unsafe)]
31 // SAFETY: `slot` is a valid `Slot` of `Pointer`. Destructuring it is safe.
32 let encoded = unsafe {
33 munge!(let Self { encoded } = slot);
34 encoded
35 };
36 match **encoded {
37 ALLOC_ABSENT_U64 => Ok(false),
38 ALLOC_PRESENT_U64 => Ok(true),
39 x => Err(DecodeError::InvalidPointerPresence(x)),
40 }
41 }
42
43 /// Encodes that a pointer is present in an output.
44 pub fn encode_present(out: &mut MaybeUninit<Self>) {
45 // `unsafe` block required in the next version of munge
46 #[allow(unused_unsafe)]
47 // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<Pointer>`.
48 // Destructuring it via `munge!` is safe.
49 let encoded = unsafe {
50 munge!(let Self { encoded } = out);
51 encoded
52 };
53 encoded.write(wire::Uint64(ALLOC_PRESENT_U64));
54 }
55
56 /// Encodes that a pointer is absent in a slot.
57 pub fn encode_absent(out: &mut MaybeUninit<Self>) {
58 // `unsafe` block required in the next version of munge
59 #[allow(unused_unsafe)]
60 // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<Pointer>`.
61 // Destructuring it via `munge!` is safe.
62 let encoded = unsafe {
63 munge!(let Self { encoded } = out);
64 encoded
65 };
66 encoded.write(wire::Uint64(ALLOC_ABSENT_U64));
67 }
68
69 /// Sets the decoded value of the pointer.
70 pub fn set_decoded(slot: Slot<'_, Self>, mut value: Slot<'de, T>) {
71 // `unsafe` block required in the next version of munge
72 #[allow(unused_unsafe)]
73 // SAFETY: `slot` is a valid `Slot` of `Pointer`. Destructuring it is safe.
74 let mut decoded = unsafe {
75 munge!(let Self { decoded } = slot);
76 decoded
77 };
78 // SAFETY: Identical to `decoded.write(ptr.into_raw())`, but raw
79 // pointers don't currently implement `IntoBytes`.
80 unsafe {
81 *decoded.as_mut_ptr() = value.as_mut_ptr();
82 }
83 }
84
85 /// Sets the decoded value of the pointer to the first element of a slice.
86 pub fn set_decoded_slice(slot: Slot<'_, Self>, mut slice: Slot<'de, [T]>) {
87 // `unsafe` block required in the next version of munge
88 #[allow(unused_unsafe)]
89 // SAFETY: `slot` is a valid `Slot` of `Pointer`. Destructuring it is safe.
90 let mut decoded = unsafe {
91 munge!(let Self { decoded } = slot);
92 decoded
93 };
94 // SAFETY: Identical to `decoded.write(ptr.into_raw())`, but raw
95 // pointers don't currently implement `IntoBytes`.
96 unsafe {
97 *decoded.as_mut_ptr() = slice.as_mut_ptr().cast();
98 }
99 }
100
101 /// Returns the underlying pointer.
102 pub fn as_ptr(&self) -> *mut T {
103 // SAFETY: Reading a raw pointer from a union is safe because raw pointers have no validity
104 // invariants. The caller must ensure the pointer is valid before dereferencing it.
105 unsafe { self.decoded }
106 }
107}