ark_api_ffi/
pod_helpers.rs

1//! POD (specifically, [`bytemuck::Pod`]) versions of some types that have invalid bit patterns
2//! for use in FFI structs.
3//!
4//! Usually, it's possible to just mark a struct containing one of the
5//! fundamental versions of these types as [`bytemuck::CheckedBitPattern`], which will allow them
6//! to pass through the relevant module host memory util casting functions by checking the relevant
7//! underlying memory bit pattern each time they get casted. However, the auto implementations of that
8//! trait are sometimes suboptimal in terms of performance, and it may be useful in certain FFI types that
9//! are:
10//!
11//! 1. particularly performance sensitive (i.e. large numbers pass through the FFI boundary per frame)
12//! 2. contain many true-[`Pod`] fields and only a few [`CheckedBitPattern`][bytemuck::CheckedBitPattern] fields
13//!
14//! to use these types instead and then only check the relevant fields for validity when they actually
15//! get used.
16
17use core::ops::Deref;
18use core::ops::DerefMut;
19
20use bytemuck::AnyBitPattern;
21use bytemuck::Pod;
22use bytemuck::Zeroable;
23
24/// A thin wrapper around u128 that ensures 16-byte alignment.
25///
26/// Since wasm is happy with 8-byte aligned `u128`, a `u128` value
27/// would sometimes get placed into an 8-byte aligned memory address
28/// by Rust when created in wasm-land, but this breaks memory safety requirements
29/// if we want to access that value directly on the host side and triggering a relevant
30/// assertion in safe casting functions. As a result,
31/// in ffi types, this should always be used over a raw `u128`
32#[repr(C, align(16))]
33#[derive(Copy, Clone, Pod, Zeroable, Default, Debug)]
34pub struct Align16U128(pub u128);
35
36impl Deref for Align16U128 {
37    type Target = u128;
38    fn deref(&self) -> &Self::Target {
39        &self.0
40    }
41}
42
43impl DerefMut for Align16U128 {
44    fn deref_mut(&mut self) -> &mut Self::Target {
45        &mut self.0
46    }
47}
48
49impl AsRef<u128> for Align16U128 {
50    fn as_ref(&self) -> &u128 {
51        &self.0
52    }
53}
54
55impl AsMut<u128> for Align16U128 {
56    fn as_mut(&mut self) -> &mut u128 {
57        &mut self.0
58    }
59}
60
61impl From<u128> for Align16U128 {
62    fn from(value: u128) -> Self {
63        Self(value)
64    }
65}
66
67impl From<Align16U128> for u128 {
68    fn from(value: Align16U128) -> Self {
69        value.0
70    }
71}
72
73impl core::fmt::Display for Align16U128 {
74    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75        write!(f, "{}", self.0)
76    }
77}
78
79/// A thin wrapper around i128 that ensures 16-byte alignment.
80///
81/// Since wasm is happy with 8-byte aligned `i128`, a `i128` value
82/// would sometimes get placed into an 8-byte aligned memory address
83/// by Rust when created in wasm-land, but this breaks memory safety requirements
84/// if we want to access that value directly on the host side and triggering a relevant
85/// assertion in safe casting functions. As a result,
86/// in ffi types, this should always be used over a raw `i128`
87#[repr(C, align(16))]
88#[derive(Copy, Clone, Pod, Zeroable, Default, Debug)]
89pub struct Align16I128(pub i128);
90
91impl Deref for Align16I128 {
92    type Target = i128;
93    fn deref(&self) -> &Self::Target {
94        &self.0
95    }
96}
97
98impl DerefMut for Align16I128 {
99    fn deref_mut(&mut self) -> &mut Self::Target {
100        &mut self.0
101    }
102}
103
104impl AsRef<i128> for Align16I128 {
105    fn as_ref(&self) -> &i128 {
106        &self.0
107    }
108}
109
110impl AsMut<i128> for Align16I128 {
111    fn as_mut(&mut self) -> &mut i128 {
112        &mut self.0
113    }
114}
115
116impl From<i128> for Align16I128 {
117    fn from(value: i128) -> Self {
118        Self(value)
119    }
120}
121
122impl From<Align16I128> for i128 {
123    fn from(value: Align16I128) -> Self {
124        value.0
125    }
126}
127
128impl core::fmt::Display for Align16I128 {
129    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130        write!(f, "{}", self.0)
131    }
132}
133
134/// A version of `bool` that has the same layout as `bool` but is [`bytemuck::Pod`].
135///
136/// In Rust, [`bool`] has a defined representation of being one byte with false being 0 and true being 1.
137/// However, it is not [`Pod`] because it is *invalid* to interpret a byte that contains any value *other*
138/// than 0 or 1 as a `bool` -- doing so is ***undefined behavior***.
139///
140/// Use this type to get around that limitation, as `PodBool` is valid for any value of `u8`, and its value is
141/// checked to be valid as a `bool` upon calling [`as_bool`][PodBool::as_bool]
142#[repr(C)]
143#[derive(Copy, Clone, Eq, PartialEq, Pod, Zeroable)]
144pub struct PodBool(u8);
145
146impl core::fmt::Debug for PodBool {
147    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148        if let Ok(b) = self.try_as_bool() {
149            write!(f, "PodBool({})", b)
150        } else {
151            write!(f, "PodBool(InvalidValue({}))", self.0)
152        }
153    }
154}
155
156impl From<bool> for PodBool {
157    fn from(v: bool) -> Self {
158        Self(u8::from(v))
159    }
160}
161
162/// Invalid value stored in [`PodBool`] when trying to interpret it as a `bool`
163pub struct InvalidPodBool {}
164
165impl PodBool {
166    pub fn as_bool(&self) -> bool {
167        match self.0 {
168            0 => false,
169            1 => true,
170            #[allow(clippy::panic)]
171            _ => panic!("invalid value in PodBool"),
172        }
173    }
174
175    pub fn try_as_bool(&self) -> Result<bool, InvalidPodBool> {
176        match self.0 {
177            0 => Ok(false),
178            1 => Ok(true),
179            _ => Err(InvalidPodBool {}),
180        }
181    }
182}
183
184/// This type adds some `const PAD` number of "explicit" or "manual" padding
185/// bytes to the end of a struct.
186///
187/// This is useful to make a type not have *real* padding bytes,
188/// and therefore be able to be marked as [`bytemuck::NoUninit`]. Specifically,
189/// it's used in the `ark_api_macros::ffi_union` macro to equalize the size of all
190/// fields of a union and therefore remove any "real" padding bytes from the union, making
191/// it safe to store in WASM memory and pass through the ark module host memory utility functions.
192/// It may also be useful in other places.
193#[derive(Copy, Clone)]
194#[repr(C)]
195pub struct TransparentPad<T, const PAD: usize>(pub T, [u8; PAD]);
196
197#[allow(unsafe_code)]
198// SAFETY: Since `[u8; N]` is always Zeroable, this is safe
199unsafe impl<T: Zeroable, const PAD: usize> Zeroable for TransparentPad<T, PAD> {}
200
201#[allow(unsafe_code)]
202// SAFETY: Since `[u8; N]` is always AnyBitPattern, this is safe
203unsafe impl<T: AnyBitPattern, const PAD: usize> AnyBitPattern for TransparentPad<T, PAD> {}
204
205#[doc(hidden)] // it is only for us in this crate
206#[macro_export]
207macro_rules! impl_checked_bit_pattern_for_transparent_pad {
208    ($inner:ident) => {
209        #[allow(unsafe_code)]
210        // SAFETY: The extra padding is always AnyBitPattern, so implies CheckedBitPattern, and this just passes
211        // down the necessary safety checks to the inner type.
212        unsafe impl<const PAD: usize> bytemuck::CheckedBitPattern
213            for $crate::TransparentPad<$inner, PAD>
214        {
215            type Bits = $crate::TransparentPad<<$inner as bytemuck::CheckedBitPattern>::Bits, PAD>;
216
217            fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
218                <$inner as bytemuck::CheckedBitPattern>::is_valid_bit_pattern(&bits.0)
219            }
220        }
221    };
222}
223
224impl<T, const PAD: usize> TransparentPad<T, PAD> {
225    pub fn new(inner: T) -> Self {
226        Self(inner, [0u8; PAD])
227    }
228}
229
230impl<T, const PAD: usize> AsRef<T> for TransparentPad<T, PAD> {
231    fn as_ref(&self) -> &T {
232        &self.0
233    }
234}
235
236impl<T, const PAD: usize> AsMut<T> for TransparentPad<T, PAD> {
237    fn as_mut(&mut self) -> &mut T {
238        &mut self.0
239    }
240}
241
242impl<T, const PAD: usize> core::ops::Deref for TransparentPad<T, PAD> {
243    type Target = T;
244
245    fn deref(&self) -> &Self::Target {
246        &self.0
247    }
248}
249
250impl<T, const PAD: usize> core::ops::DerefMut for TransparentPad<T, PAD> {
251    fn deref_mut(&mut self) -> &mut Self::Target {
252        &mut self.0
253    }
254}