typhoon_context/args/
bytemuck.rs

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