Skip to content

Commit cc8e9ef

Browse files
committed
Improve IntrospectedInstruction
1 parent bb90c38 commit cc8e9ef

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

sdk/src/sysvars/instructions.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ where
4242

4343
/// Load the number of instructions in the currently executing `Transaction`.
4444
#[inline(always)]
45-
pub fn num_instructions(&self) -> u16 {
45+
pub fn num_instructions(&self) -> usize {
4646
// SAFETY: The first 2 bytes of the Instructions sysvar data represents the
4747
// number of instructions.
48-
unsafe { u16::from_le_bytes(*(self.data.as_ptr() as *const [u8; 2])) }
48+
u16::from_le_bytes(unsafe { *(self.data.as_ptr() as *const [u8; 2]) }) as usize
4949
}
5050

5151
/// Load the current `Instruction`'s index in the currently executing
@@ -75,10 +75,7 @@ where
7575
.as_ptr()
7676
.add(size_of::<u16>() + index * size_of::<u16>()) as *const u16);
7777

78-
IntrospectedInstruction {
79-
raw: self.data.as_ptr().add(offset as usize),
80-
marker: PhantomData,
81-
}
78+
IntrospectedInstruction::new_unchecked(self.data.as_ptr().add(offset as usize))
8279
}
8380

8481
/// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index.
@@ -87,7 +84,7 @@ where
8784
&self,
8885
index: usize,
8986
) -> Result<IntrospectedInstruction, ProgramError> {
90-
if index >= self.num_instructions() as usize {
87+
if index >= self.num_instructions() {
9188
return Err(ProgramError::InvalidInstructionData);
9289
}
9390

@@ -133,10 +130,34 @@ impl<'a> TryFrom<&'a AccountView> for Instructions<Ref<'a, [u8]>> {
133130
#[derive(Clone, Debug, Eq, PartialEq)]
134131
pub struct IntrospectedInstruction<'a> {
135132
pub raw: *const u8,
136-
pub marker: PhantomData<&'a [u8]>,
133+
marker: PhantomData<&'a [u8]>,
137134
}
138135

139136
impl IntrospectedInstruction<'_> {
137+
/// Create a new IntrospectedInstruction.
138+
///
139+
/// # Safety
140+
///
141+
/// This function is unsafe because it does not verify anything about the pointer.
142+
///
143+
/// It is private and used internally within the `get_instruction_account_at` function, which
144+
/// performs the necessary index verification. However, to optimize performance for users
145+
/// who are sure that the index is in bounds, we have exposed it as an unsafe function.
146+
#[inline(always)]
147+
unsafe fn new_unchecked(raw: *const u8) -> Self {
148+
Self {
149+
raw,
150+
marker: PhantomData,
151+
}
152+
}
153+
154+
/// Load the number of instructions in the currently executing `Transaction`.
155+
#[inline(always)]
156+
pub fn num_account_metas(&self) -> usize {
157+
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
158+
u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
159+
}
160+
140161
/// Get the instruction account at the specified index.
141162
///
142163
/// # Safety
@@ -166,9 +187,9 @@ impl IntrospectedInstruction<'_> {
166187
index: usize,
167188
) -> Result<&IntrospectedInstructionAccount, ProgramError> {
168189
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
169-
let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
190+
let num_accounts = self.num_account_metas();
170191

171-
if index >= num_accounts as usize {
192+
if index >= num_accounts {
172193
return Err(ProgramError::InvalidArgument);
173194
}
174195

@@ -180,24 +201,23 @@ impl IntrospectedInstruction<'_> {
180201
#[inline(always)]
181202
pub fn get_program_id(&self) -> &Address {
182203
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
183-
let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
204+
let num_accounts = self.num_account_metas();
184205

185206
// SAFETY: The program ID is located after the instruction accounts.
186207
unsafe {
187-
&*(self.raw.add(
188-
size_of::<u16>()
189-
+ num_accounts as usize * size_of::<IntrospectedInstructionAccount>(),
190-
) as *const Address)
208+
&*(self
209+
.raw
210+
.add(size_of::<u16>() + num_accounts * size_of::<IntrospectedInstructionAccount>())
211+
as *const Address)
191212
}
192213
}
193214

194215
/// Get the instruction data of the `Instruction`.
195216
#[inline(always)]
196217
pub fn get_instruction_data(&self) -> &[u8] {
197218
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
198-
let offset = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
199-
* size_of::<IntrospectedInstructionAccount>()
200-
+ ADDRESS_BYTES;
219+
let offset =
220+
self.num_account_metas() * size_of::<IntrospectedInstructionAccount>() + ADDRESS_BYTES;
201221

202222
// SAFETY: The instruction data length is located after the program ID.
203223
let data_len = u16::from_le_bytes(unsafe {

0 commit comments

Comments
 (0)