Skip to main content

typhoon_context/args/
bytemuck.rs

1use {
2    crate::HandlerContext,
3    bytemuck::AnyBitPattern,
4    solana_account_view::AccountView,
5    solana_address::Address,
6    typhoon_errors::{Error, ErrorCode},
7};
8
9#[derive(Debug)]
10pub struct Arg<'a, T>(pub &'a T);
11
12impl<'c, T> HandlerContext<'_, '_, 'c> for Arg<'c, T>
13where
14    T: AnyBitPattern,
15{
16    #[inline(always)]
17    fn from_entrypoint(
18        _program_id: &Address,
19        _accounts: &mut &[AccountView],
20        instruction_data: &mut &'c [u8],
21    ) -> Result<Self, Error> {
22        let len = core::mem::size_of::<T>();
23
24        if len > instruction_data.len() {
25            return Err(ErrorCode::InvalidDataLength.into());
26        }
27
28        // SAFETY: The invariant `len <= instruction_data.len()` is upheld by the preceding
29        // bounds check, ensuring that the split index is within the valid range [0, len]
30        // where len does not exceed the slice length, thus satisfying the preconditions
31        // for `split_at_unchecked`.
32        let (arg_data, remaining) = unsafe { instruction_data.split_at_unchecked(len) };
33        let data_ptr = arg_data.as_ptr();
34
35        if data_ptr.align_offset(core::mem::align_of::<T>()) != 0 {
36            return Err(ErrorCode::InvalidDataAlignment.into());
37        }
38
39        let arg: &T = unsafe { &*(data_ptr as *const T) };
40
41        *instruction_data = remaining;
42
43        Ok(Arg(arg))
44    }
45}