wasmi_core/
untyped.rs

1#[cfg(feature = "simd")]
2use crate::V128;
3use crate::{F32, F64};
4use core::{
5    error::Error,
6    fmt::{self, Display},
7};
8
9/// An untyped value.
10///
11/// Provides a dense and simple interface to all functional Wasm operations.
12#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
13#[cfg_attr(not(feature = "simd"), repr(transparent))]
14#[cfg_attr(feature = "simd", repr(C))]
15pub struct UntypedVal {
16    /// The low 64-bits of an [`UntypedVal`].
17    ///
18    /// The low 64-bits are used to encode and decode all types that
19    /// are convertible from and to an [`UntypedVal`] that fit into
20    /// 64-bits such as `i32`, `i64`, `f32` and `f64`.
21    pub(crate) lo64: u64,
22    /// The high 64-bits of an [`UntypedVal`].
23    ///
24    /// This is only used to encode or decode types which do not fit
25    /// into the lower 64-bits part such as Wasm's `V128` or `i128`.
26    #[cfg(feature = "simd")]
27    pub(crate) hi64: u64,
28}
29
30/// Implemented by types that can be read (or decoded) as `T`.
31///
32/// Mainly implemented by [`UntypedVal`].
33pub trait ReadAs<T> {
34    /// Reads `self` as value of type `T`.
35    fn read_as(&self) -> T;
36}
37
38macro_rules! impl_read_as_for_int {
39    ( $( $int:ty ),* $(,)? ) => {
40        $(
41            impl ReadAs<$int> for UntypedVal {
42                fn read_as(&self) -> $int {
43                    self.read_lo64() as $int
44                }
45            }
46        )*
47    };
48}
49impl_read_as_for_int!(i8, i16, i32, i64, u8, u16, u32, u64);
50
51macro_rules! impl_read_as_for_float {
52    ( $( $float:ty ),* $(,)? ) => {
53        $(
54            impl ReadAs<$float> for UntypedVal {
55                fn read_as(&self) -> $float {
56                    <$float>::from_bits(self.read_lo64() as _)
57                }
58            }
59        )*
60    };
61}
62impl_read_as_for_float!(f32, f64);
63
64#[cfg(feature = "simd")]
65impl ReadAs<V128> for UntypedVal {
66    fn read_as(&self) -> V128 {
67        // Note: we can re-use the `From` impl since both types are of equal size.
68        V128::from(*self)
69    }
70}
71
72impl ReadAs<bool> for UntypedVal {
73    fn read_as(&self) -> bool {
74        self.read_lo64() != 0
75    }
76}
77
78/// Implemented by types that can be written to (or encoded) as `T`.
79///
80/// Mainly implemented by [`UntypedVal`].
81pub trait WriteAs<T> {
82    /// Writes to `self` as value of type `T`.
83    fn write_as(&mut self, value: T);
84}
85
86macro_rules! impl_write_as_for_int {
87    ( $( $int:ty as $as:ty ),* $(,)? ) => {
88        $(
89            impl WriteAs<$int> for UntypedVal {
90                #[allow(clippy::cast_lossless)]
91                fn write_as(&mut self, value: $int) {
92                    self.write_lo64(value as $as as _)
93                }
94            }
95
96            impl WriteAs<::core::num::NonZero<$int>> for UntypedVal {
97                fn write_as(&mut self, value: ::core::num::NonZero<$int>) {
98                    <UntypedVal as WriteAs<$int>>::write_as(self, value.get())
99                }
100            }
101        )*
102    };
103}
104impl_write_as_for_int!(i8 as u8, i16 as u16, i32 as u32, i64 as u64);
105
106macro_rules! impl_write_as_for_uint {
107    ( $( $int:ty ),* $(,)? ) => {
108        $(
109            impl WriteAs<$int> for UntypedVal {
110                #[allow(clippy::cast_lossless)]
111                fn write_as(&mut self, value: $int) {
112                    self.write_lo64(value as _)
113                }
114            }
115
116            impl WriteAs<::core::num::NonZero<$int>> for UntypedVal {
117                fn write_as(&mut self, value: ::core::num::NonZero<$int>) {
118                    <UntypedVal as WriteAs<$int>>::write_as(self, value.get())
119                }
120            }
121        )*
122    };
123}
124impl_write_as_for_uint!(u8, u16, u32, u64);
125
126impl WriteAs<bool> for UntypedVal {
127    #[allow(clippy::cast_lossless)]
128    fn write_as(&mut self, value: bool) {
129        self.write_lo64(value as _)
130    }
131}
132
133macro_rules! impl_write_as_for_float {
134    ( $( $float:ty ),* $(,)? ) => {
135        $(
136            impl WriteAs<$float> for UntypedVal {
137                #[allow(clippy::cast_lossless)]
138                fn write_as(&mut self, value: $float) {
139                    self.write_lo64(<$float>::to_bits(value) as _)
140                }
141            }
142        )*
143    };
144}
145impl_write_as_for_float!(f32, f64);
146
147#[cfg(feature = "simd")]
148impl WriteAs<V128> for UntypedVal {
149    fn write_as(&mut self, value: V128) {
150        // Note: we can re-use the `From` impl since both types are of equal size.
151        *self = UntypedVal::from(value);
152    }
153}
154
155impl UntypedVal {
156    /// Reads the low 64-bit of the [`UntypedVal`].
157    ///
158    /// In contract to [`UntypedVal::to_bits64`] this ignores the high-bits entirely.
159    fn read_lo64(&self) -> u64 {
160        self.lo64
161    }
162
163    /// Writes the low 64-bit of the [`UntypedVal`].
164    fn write_lo64(&mut self, bits: u64) {
165        self.lo64 = bits;
166    }
167
168    /// Creates an [`UntypedVal`] from the given lower 64-bit bits.
169    ///
170    /// This sets the high 64-bits to zero if any.
171    pub const fn from_bits64(lo64: u64) -> Self {
172        Self {
173            lo64,
174            #[cfg(feature = "simd")]
175            hi64: 0,
176        }
177    }
178
179    /// Returns the underlying lower 64-bits of the [`UntypedVal`].
180    ///
181    /// This ignores the high 64-bits of the [`UntypedVal`] if any.
182    pub const fn to_bits64(self) -> u64 {
183        self.lo64
184    }
185}
186
187macro_rules! impl_from_untyped_for_int {
188    ( $( $int:ty ),* $(,)? ) => {
189        $(
190            impl From<UntypedVal> for $int {
191                fn from(untyped: UntypedVal) -> Self {
192                    untyped.to_bits64() as _
193                }
194            }
195        )*
196    };
197}
198impl_from_untyped_for_int!(i8, i16, i32, i64, u8, u16, u32, u64);
199
200macro_rules! impl_from_untyped_for_float {
201    ( $( $float:ty ),* $(,)? ) => {
202        $(
203            impl From<UntypedVal> for $float {
204                fn from(untyped: UntypedVal) -> Self {
205                    Self::from_bits(untyped.to_bits64() as _)
206                }
207            }
208        )*
209    };
210}
211impl_from_untyped_for_float!(f32, f64, F32, F64);
212
213#[cfg(feature = "simd")]
214impl From<UntypedVal> for V128 {
215    fn from(value: UntypedVal) -> Self {
216        let u128 = (u128::from(value.hi64) << 64) | (u128::from(value.lo64));
217        Self::from(u128)
218    }
219}
220
221#[cfg(feature = "simd")]
222impl From<V128> for UntypedVal {
223    fn from(value: V128) -> Self {
224        let u128 = value.as_u128();
225        let lo64 = u128 as u64;
226        let hi64 = (u128 >> 64) as u64;
227        Self { lo64, hi64 }
228    }
229}
230
231impl From<UntypedVal> for bool {
232    fn from(untyped: UntypedVal) -> Self {
233        untyped.to_bits64() != 0
234    }
235}
236
237macro_rules! impl_from_unsigned_prim {
238    ( $( $prim:ty ),* $(,)? ) => {
239        $(
240            impl From<$prim> for UntypedVal {
241                #[allow(clippy::cast_lossless)]
242                fn from(value: $prim) -> Self {
243                    Self::from_bits64(value as _)
244                }
245            }
246
247            impl From<::core::num::NonZero<$prim>> for UntypedVal {
248                fn from(value: ::core::num::NonZero<$prim>) -> Self {
249                    <_ as From<$prim>>::from(value.get())
250                }
251            }
252        )*
253    };
254}
255#[rustfmt::skip]
256impl_from_unsigned_prim!(
257    u8, u16, u32, u64,
258);
259
260impl From<bool> for UntypedVal {
261    #[allow(clippy::cast_lossless)]
262    fn from(value: bool) -> Self {
263        Self::from_bits64(value as _)
264    }
265}
266
267macro_rules! impl_from_signed_prim {
268    ( $( $prim:ty as $base:ty ),* $(,)? ) => {
269        $(
270            impl From<$prim> for UntypedVal {
271                #[allow(clippy::cast_lossless)]
272                fn from(value: $prim) -> Self {
273                    Self::from_bits64(u64::from(value as $base))
274                }
275            }
276
277            impl From<::core::num::NonZero<$prim>> for UntypedVal {
278                fn from(value: ::core::num::NonZero<$prim>) -> Self {
279                    <_ as From<$prim>>::from(value.get())
280                }
281            }
282        )*
283    };
284}
285#[rustfmt::skip]
286impl_from_signed_prim!(
287    i8 as u8,
288    i16 as u16,
289    i32 as u32,
290    i64 as u64,
291);
292
293macro_rules! impl_from_float {
294    ( $( $float:ty ),* $(,)? ) => {
295        $(
296            impl From<$float> for UntypedVal {
297                fn from(value: $float) -> Self {
298                    Self::from_bits64(u64::from(value.to_bits()))
299                }
300            }
301        )*
302    };
303}
304impl_from_float!(f32, f64, F32, F64);
305
306/// Macro to help implement generic trait implementations for tuple types.
307macro_rules! for_each_tuple {
308    ($mac:ident) => {
309        $mac!( 0 );
310        $mac!( 1 T1);
311        $mac!( 2 T1 T2);
312        $mac!( 3 T1 T2 T3);
313        $mac!( 4 T1 T2 T3 T4);
314        $mac!( 5 T1 T2 T3 T4 T5);
315        $mac!( 6 T1 T2 T3 T4 T5 T6);
316        $mac!( 7 T1 T2 T3 T4 T5 T6 T7);
317        $mac!( 8 T1 T2 T3 T4 T5 T6 T7 T8);
318        $mac!( 9 T1 T2 T3 T4 T5 T6 T7 T8 T9);
319        $mac!(10 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10);
320        $mac!(11 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11);
321        $mac!(12 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12);
322        $mac!(13 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13);
323        $mac!(14 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14);
324        $mac!(15 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
325        $mac!(16 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16);
326    }
327}
328
329/// An error that may occur upon encoding or decoding slices of [`UntypedVal`].
330#[derive(Debug, Copy, Clone)]
331pub enum UntypedError {
332    /// The [`UntypedVal`] slice length did not match `Self`.
333    InvalidLen,
334}
335
336impl UntypedError {
337    /// Creates a new `InvalidLen` [`UntypedError`].
338    #[cold]
339    pub fn invalid_len() -> Self {
340        Self::InvalidLen
341    }
342}
343
344impl Error for UntypedError {}
345
346impl Display for UntypedError {
347    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348        match self {
349            UntypedError::InvalidLen => {
350                write!(f, "mismatched length of the untyped slice",)
351            }
352        }
353    }
354}
355
356impl UntypedVal {
357    /// Decodes the slice of [`UntypedVal`] as a value of type `T`.
358    ///
359    /// # Note
360    ///
361    /// `T` can either be a single type or a tuple of types depending
362    /// on the length of the `slice`.
363    ///
364    /// # Errors
365    ///
366    /// If the tuple length of `T` and the length of `slice` does not match.
367    pub fn decode_slice<T>(slice: &[Self]) -> Result<T, UntypedError>
368    where
369        T: DecodeUntypedSlice,
370    {
371        <T as DecodeUntypedSlice>::decode_untyped_slice(slice)
372    }
373
374    /// Encodes the slice of [`UntypedVal`] from the given value of type `T`.
375    ///
376    /// # Note
377    ///
378    /// `T` can either be a single type or a tuple of types depending
379    /// on the length of the `slice`.
380    ///
381    /// # Errors
382    ///
383    /// If the tuple length of `T` and the length of `slice` does not match.
384    pub fn encode_slice<T>(slice: &mut [Self], input: T) -> Result<(), UntypedError>
385    where
386        T: EncodeUntypedSlice,
387    {
388        <T as EncodeUntypedSlice>::encode_untyped_slice(input, slice)
389    }
390}
391
392/// Tuple types that allow to decode a slice of [`UntypedVal`].
393pub trait DecodeUntypedSlice: Sized {
394    /// Decodes the slice of [`UntypedVal`] as a value of type `Self`.
395    ///
396    /// # Note
397    ///
398    /// `Self` can either be a single type or a tuple of types depending
399    /// on the length of the `slice`.
400    ///
401    /// # Errors
402    ///
403    /// If the tuple length of `Self` and the length of `slice` does not match.
404    fn decode_untyped_slice(params: &[UntypedVal]) -> Result<Self, UntypedError>;
405}
406
407impl<T1> DecodeUntypedSlice for T1
408where
409    T1: From<UntypedVal>,
410{
411    #[inline]
412    fn decode_untyped_slice(results: &[UntypedVal]) -> Result<Self, UntypedError> {
413        <(T1,) as DecodeUntypedSlice>::decode_untyped_slice(results).map(|t| t.0)
414    }
415}
416
417macro_rules! impl_decode_untyped_slice {
418    ( $n:literal $( $tuple:ident )* ) => {
419        impl<$($tuple),*> DecodeUntypedSlice for ($($tuple,)*)
420        where
421            $(
422                $tuple: From<UntypedVal>
423            ),*
424        {
425            #[allow(non_snake_case)]
426            #[inline]
427            fn decode_untyped_slice(results: &[UntypedVal]) -> Result<Self, UntypedError> {
428                match results {
429                    &[ $($tuple),* ] => Ok((
430                        $(
431                            <$tuple as From<UntypedVal>>::from($tuple),
432                        )*
433                    )),
434                    _ => Err(UntypedError::invalid_len()),
435                }
436            }
437        }
438    };
439}
440for_each_tuple!(impl_decode_untyped_slice);
441
442/// Tuple types that allow to encode a slice of [`UntypedVal`].
443pub trait EncodeUntypedSlice {
444    /// Encodes the slice of [`UntypedVal`] from the given value of type `Self`.
445    ///
446    /// # Note
447    ///
448    /// `Self` can either be a single type or a tuple of types depending
449    /// on the length of the `slice`.
450    ///
451    /// # Errors
452    ///
453    /// If the tuple length of `Self` and the length of `slice` does not match.
454    fn encode_untyped_slice(self, results: &mut [UntypedVal]) -> Result<(), UntypedError>;
455}
456
457impl<T1> EncodeUntypedSlice for T1
458where
459    T1: Into<UntypedVal>,
460{
461    #[inline]
462    fn encode_untyped_slice(self, results: &mut [UntypedVal]) -> Result<(), UntypedError> {
463        <(T1,) as EncodeUntypedSlice>::encode_untyped_slice((self,), results)
464    }
465}
466
467macro_rules! impl_encode_untyped_slice {
468    ( $n:literal $( $tuple:ident )* ) => {
469        impl<$($tuple),*> EncodeUntypedSlice for ($($tuple,)*)
470        where
471            $(
472                $tuple: Into<UntypedVal>
473            ),*
474        {
475            #[allow(non_snake_case)]
476            #[inline]
477            fn encode_untyped_slice<'a>(self, results: &'a mut [UntypedVal]) -> Result<(), UntypedError> {
478                let Ok(_results) = <&'a mut [UntypedVal; $n]>::try_from(results) else {
479                    return Err(UntypedError::invalid_len())
480                };
481                let ( $( $tuple ,)* ) = self;
482                let mut _i = 0;
483                $(
484                    _results[_i] = <$tuple as Into<UntypedVal>>::into($tuple);
485                    _i += 1;
486                )*
487                Ok(())
488            }
489        }
490    };
491}
492for_each_tuple!(impl_encode_untyped_slice);