|
@@ -43,16 +43,17 @@ const STATIC_ACCOUNT_DATA: usize = size_of::<Account>() + MAX_PERMITTED_DATA_INC
|
|
|
|
|
|
|
|
/// Declare the program entrypoint and set up global handlers.
|
|
/// Declare the program entrypoint and set up global handlers.
|
|
|
///
|
|
///
|
|
|
-/// The main difference from the standard (SDK) [`entrypoint`](https://docs.rs/solana-program-entrypoint/latest/solana_program_entrypoint/macro.entrypoint.html)
|
|
|
|
|
-/// macro is that this macro represents an entrypoint that does not perform allocations or copies
|
|
|
|
|
-/// when reading the input buffer.
|
|
|
|
|
|
|
+/// The main difference from the standard (SDK) [`entrypoint`] macro is that this macro represents
|
|
|
|
|
+/// an entrypoint that does not perform allocations or copies when reading the input buffer.
|
|
|
///
|
|
///
|
|
|
-/// This macro emits the common boilerplate necessary to begin program execution, calling a
|
|
|
|
|
-/// provided function to process the program instruction supplied by the runtime, and reporting
|
|
|
|
|
-/// its result to the runtime.
|
|
|
|
|
|
|
+/// [`entrypoint`]: https://docs.rs/solana-program-entrypoint/latest/solana_program_entrypoint/macro.entrypoint.html
|
|
|
///
|
|
///
|
|
|
-/// It also sets up a [global allocator] and [panic handler], using the [`crate::default_allocator!`]
|
|
|
|
|
-/// and [`crate::default_panic_handler!`] macros.
|
|
|
|
|
|
|
+/// This macro emits the common boilerplate necessary to begin program execution, calling a provided
|
|
|
|
|
+/// function to process the program instruction supplied by the runtime, and reporting its result to
|
|
|
|
|
+/// the runtime.
|
|
|
|
|
+///
|
|
|
|
|
+/// It also sets up a [global allocator] and [panic handler], using the
|
|
|
|
|
+/// [`crate::default_allocator!`] and [`crate::default_panic_handler!`] macros.
|
|
|
///
|
|
///
|
|
|
/// The first argument is the name of a function with this type signature:
|
|
/// The first argument is the name of a function with this type signature:
|
|
|
///
|
|
///
|
|
@@ -67,9 +68,9 @@ const STATIC_ACCOUNT_DATA: usize = size_of::<Account>() + MAX_PERMITTED_DATA_INC
|
|
|
/// identifiers in the current scope.
|
|
/// identifiers in the current scope.
|
|
|
///
|
|
///
|
|
|
/// There is a second optional argument that allows to specify the maximum number of accounts
|
|
/// There is a second optional argument that allows to specify the maximum number of accounts
|
|
|
-/// expected by instructions of the program. This is useful to reduce the stack size requirement
|
|
|
|
|
-/// for the entrypoint, as the default is set to [`crate::MAX_TX_ACCOUNTS`]. If the program
|
|
|
|
|
-/// receives more accounts than the specified maximum, these accounts will be ignored.
|
|
|
|
|
|
|
+/// expected by instructions of the program. This is useful to reduce the stack size requirement for
|
|
|
|
|
+/// the entrypoint, as the default is set to [`crate::MAX_TX_ACCOUNTS`]. If the program receives
|
|
|
|
|
+/// more accounts than the specified maximum, these accounts will be ignored.
|
|
|
///
|
|
///
|
|
|
/// [global allocator]: https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html
|
|
/// [global allocator]: https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html
|
|
|
/// [maximum number of accounts]: https://github.com/anza-xyz/agave/blob/ccabfcf84921977202fd06d3197cbcea83742133/runtime/src/bank.rs#L3207-L3219
|
|
/// [maximum number of accounts]: https://github.com/anza-xyz/agave/blob/ccabfcf84921977202fd06d3197cbcea83742133/runtime/src/bank.rs#L3207-L3219
|
|
@@ -109,15 +110,17 @@ const STATIC_ACCOUNT_DATA: usize = size_of::<Account>() + MAX_PERMITTED_DATA_INC
|
|
|
/// # Important
|
|
/// # Important
|
|
|
///
|
|
///
|
|
|
/// The panic handler set up is different depending on whether the `std` library is available to the
|
|
/// The panic handler set up is different depending on whether the `std` library is available to the
|
|
|
-/// linker or not. The `entrypoint` macro will set up a default
|
|
|
|
|
-/// panic "hook", that works with the `#[panic_handler]` set by the `std`. Therefore, this macro
|
|
|
|
|
-/// should be used when the program or any of its dependencies are dependent on the `std` library.
|
|
|
|
|
|
|
+/// linker or not. The `entrypoint` macro will set up a default panic "hook", that works with the
|
|
|
|
|
+/// `#[panic_handler]` set by the `std`. Therefore, this macro should be used when the program or
|
|
|
|
|
+/// any of its dependencies are dependent on the `std` library.
|
|
|
///
|
|
///
|
|
|
/// When the program and all its dependencies are `no_std`, it is necessary to set a
|
|
/// When the program and all its dependencies are `no_std`, it is necessary to set a
|
|
|
-/// `#[panic_handler]` to handle panics. This is done by the [`crate::nostd_panic_handler`](https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html)
|
|
|
|
|
-/// macro. In this case, it is not possible to use the `entrypoint`
|
|
|
|
|
-/// macro. Use the [`crate::program_entrypoint!`] macro instead and set up the allocator and panic
|
|
|
|
|
-/// handler manually.
|
|
|
|
|
|
|
+/// `#[panic_handler]` to handle panics. This is done by the [`crate::nostd_panic_handler`] macro.
|
|
|
|
|
+/// In this case, it is not possible to use the `entrypoint` macro. Use the
|
|
|
|
|
+/// [`crate::program_entrypoint!`] macro instead and set up the allocator and panic handler
|
|
|
|
|
+/// manually.
|
|
|
|
|
+///
|
|
|
|
|
+/// [`crate::nostd_panic_handler`]: https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html
|
|
|
#[macro_export]
|
|
#[macro_export]
|
|
|
macro_rules! entrypoint {
|
|
macro_rules! entrypoint {
|
|
|
( $process_instruction:expr ) => {
|
|
( $process_instruction:expr ) => {
|
|
@@ -132,9 +135,9 @@ macro_rules! entrypoint {
|
|
|
|
|
|
|
|
/// Declare the program entrypoint.
|
|
/// Declare the program entrypoint.
|
|
|
///
|
|
///
|
|
|
-/// This macro is similar to the [`crate::entrypoint!`] macro, but it does
|
|
|
|
|
-/// not set up a global allocator nor a panic handler. This is useful when the program will set up
|
|
|
|
|
-/// its own allocator and panic handler.
|
|
|
|
|
|
|
+/// This macro is similar to the [`crate::entrypoint!`] macro, but it does not set up a global
|
|
|
|
|
+/// allocator nor a panic handler. This is useful when the program will set up its own allocator and
|
|
|
|
|
+/// panic handler.
|
|
|
///
|
|
///
|
|
|
/// The first argument is the name of a function with this type signature:
|
|
/// The first argument is the name of a function with this type signature:
|
|
|
///
|
|
///
|
|
@@ -149,9 +152,9 @@ macro_rules! entrypoint {
|
|
|
/// identifiers in the current scope.
|
|
/// identifiers in the current scope.
|
|
|
///
|
|
///
|
|
|
/// There is a second optional argument that allows to specify the maximum number of accounts
|
|
/// There is a second optional argument that allows to specify the maximum number of accounts
|
|
|
-/// expected by instructions of the program. This is useful to reduce the stack size requirement
|
|
|
|
|
-/// for the entrypoint, as the default is set to [`crate::MAX_TX_ACCOUNTS`]. If the program
|
|
|
|
|
-/// receives more accounts than the specified maximum, these accounts will be ignored.
|
|
|
|
|
|
|
+/// expected by instructions of the program. This is useful to reduce the stack size requirement for
|
|
|
|
|
+/// the entrypoint, as the default is set to [`MAX_TX_ACCOUNTS`]. If the program receives more
|
|
|
|
|
+/// accounts than the specified maximum, these accounts will be ignored.
|
|
|
#[macro_export]
|
|
#[macro_export]
|
|
|
macro_rules! program_entrypoint {
|
|
macro_rules! program_entrypoint {
|
|
|
( $process_instruction:expr ) => {
|
|
( $process_instruction:expr ) => {
|
|
@@ -183,7 +186,7 @@ macro_rules! program_entrypoint {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/// Align a pointer to the BPF alignment of `u128`.
|
|
|
|
|
|
|
+/// Align a pointer to the BPF alignment of [`u128`].
|
|
|
macro_rules! align_pointer {
|
|
macro_rules! align_pointer {
|
|
|
($ptr:ident) => {
|
|
($ptr:ident) => {
|
|
|
// integer-to-pointer cast: the resulting pointer will have the same provenance as
|
|
// integer-to-pointer cast: the resulting pointer will have the same provenance as
|
|
@@ -192,16 +195,14 @@ macro_rules! align_pointer {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/// A macro to repeat a pattern to process an account `n` times,
|
|
|
|
|
-/// where `n` is the number of `_` tokens in the input.
|
|
|
|
|
|
|
+/// A macro to repeat a pattern to process an account `n` times, where `n` is the number of `_`
|
|
|
|
|
+/// tokens in the input.
|
|
|
///
|
|
///
|
|
|
-/// The main advantage of this macro is to inline the code to process
|
|
|
|
|
-/// `n` accounts, which is useful to reduce the number of jumps required.
|
|
|
|
|
-/// As a result, it reduces the number of CUs required to process each
|
|
|
|
|
-/// account.
|
|
|
|
|
|
|
+/// The main advantage of this macro is to inline the code to process `n` accounts, which is useful
|
|
|
|
|
+/// to reduce the number of jumps required. As a result, it reduces the number of CUs required to
|
|
|
|
|
+/// process each account.
|
|
|
///
|
|
///
|
|
|
-/// Note that this macro emits code to update both the `input` and
|
|
|
|
|
-/// `accounts` pointers.
|
|
|
|
|
|
|
+/// Note that this macro emits code to update both the `input` and `accounts` pointers.
|
|
|
macro_rules! process_n_accounts {
|
|
macro_rules! process_n_accounts {
|
|
|
// Base case: no tokens left.
|
|
// Base case: no tokens left.
|
|
|
( () => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {};
|
|
( () => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {};
|
|
@@ -236,8 +237,8 @@ macro_rules! process_n_accounts {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/// Convenience macro to transform the number of accounts to process
|
|
|
|
|
-/// into a pattern of `_` tokens for the [`process_n_accounts`] macro.
|
|
|
|
|
|
|
+/// Convenience macro to transform the number of accounts to process into a pattern of `_` tokens
|
|
|
|
|
+/// for the [`process_n_accounts`] macro.
|
|
|
macro_rules! process_accounts {
|
|
macro_rules! process_accounts {
|
|
|
( 1 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
|
|
( 1 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
|
|
|
process_n_accounts!( (_) => ( $input, $accounts, $accounts_slice ));
|
|
process_n_accounts!( (_) => ( $input, $accounts, $accounts_slice ));
|
|
@@ -256,21 +257,19 @@ macro_rules! process_accounts {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/// Create an `AccountInfo` referencing the same account referenced
|
|
|
|
|
-/// by the `AccountInfo` at the specified `index`.
|
|
|
|
|
|
|
+/// Create an [`AccountInfo`] referencing the same account referenced by the [`AccountInfo`] at the
|
|
|
|
|
+/// specified `index`.
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
/// The caller must ensure that:
|
|
/// The caller must ensure that:
|
|
|
-/// - `accounts` pointer must point to an array of `AccountInfo`s where
|
|
|
|
|
-/// the new `AccountInfo` will be written.
|
|
|
|
|
-/// - `accounts_slice` pointer must point to a slice of `AccountInfo`s
|
|
|
|
|
-/// already initialized.
|
|
|
|
|
|
|
+/// - `accounts` pointer must point to an array of [`AccountInfo`]s where the new [`AccountInfo`]
|
|
|
|
|
+/// will be written.
|
|
|
|
|
+/// - `accounts_slice` pointer must point to a slice of [`AccountInfo`]s already initialized.
|
|
|
/// - `index` is a valid index in the `accounts_slice`.
|
|
/// - `index` is a valid index in the `accounts_slice`.
|
|
|
//
|
|
//
|
|
|
-// Note: The function is marked as `cold` to stop the compiler from optimizing the
|
|
|
|
|
-// parsing of duplicated accounts, which leads to an overall increase in CU
|
|
|
|
|
-// consumption.
|
|
|
|
|
|
|
+// Note: The function is marked as `cold` to stop the compiler from optimizing the parsing of
|
|
|
|
|
+// duplicated accounts, which leads to an overall increase in CU consumption.
|
|
|
#[cold]
|
|
#[cold]
|
|
|
#[inline(always)]
|
|
#[inline(always)]
|
|
|
unsafe fn clone_account_info(
|
|
unsafe fn clone_account_info(
|
|
@@ -285,17 +284,16 @@ unsafe fn clone_account_info(
|
|
|
|
|
|
|
|
/// Parse the arguments from the runtime input buffer.
|
|
/// Parse the arguments from the runtime input buffer.
|
|
|
///
|
|
///
|
|
|
-/// This function parses the `accounts`, `instruction_data` and `program_id` from
|
|
|
|
|
-/// the input buffer. The `MAX_ACCOUNTS` constant defines the maximum number of accounts
|
|
|
|
|
-/// that can be parsed from the input buffer. If the number of accounts in the input buffer
|
|
|
|
|
-/// exceeds `MAX_ACCOUNTS`, the excess accounts will be skipped (ignored).
|
|
|
|
|
|
|
+/// This function parses the `accounts`, `instruction_data` and `program_id` from the input buffer.
|
|
|
|
|
+/// The `MAX_ACCOUNTS` constant defines the maximum number of accounts that can be parsed from the
|
|
|
|
|
+/// input buffer. If the number of accounts in the input buffer exceeds `MAX_ACCOUNTS`, the excess
|
|
|
|
|
+/// accounts will be skipped (ignored).
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
-/// The caller must ensure that the `input` buffer is valid, i.e., it represents the
|
|
|
|
|
-/// program input parameters serialized by the SVM loader. Additionally, the `input`
|
|
|
|
|
-/// should last for the lifetime of the program execution since the returned values
|
|
|
|
|
-/// reference the `input`.
|
|
|
|
|
|
|
+/// The caller must ensure that the `input` buffer is valid, i.e., it represents the program input
|
|
|
|
|
+/// parameters serialized by the SVM loader. Additionally, the `input` should last for the lifetime
|
|
|
|
|
+/// of the program execution since the returned values reference the `input`.
|
|
|
#[inline(always)]
|
|
#[inline(always)]
|
|
|
pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
|
mut input: *mut u8,
|
|
mut input: *mut u8,
|
|
@@ -331,15 +329,15 @@ pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
|
|
|
|
|
|
if processed > 1 {
|
|
if processed > 1 {
|
|
|
// The number of accounts to process (`to_process_plus_one`) is limited to
|
|
// The number of accounts to process (`to_process_plus_one`) is limited to
|
|
|
- // `MAX_ACCOUNTS`, which is the capacity of the accounts array. When there
|
|
|
|
|
- // are more accounts to process than the maximum, we still need to skip the
|
|
|
|
|
- // remaining accounts (`to_skip`) to move the input pointer to the instruction
|
|
|
|
|
- // data. At the end, we return the number of accounts processed (`processed`),
|
|
|
|
|
- // which represents the accounts initialized in the `accounts` slice.
|
|
|
|
|
|
|
+ // `MAX_ACCOUNTS`, which is the capacity of the accounts array. When there are more
|
|
|
|
|
+ // accounts to process than the maximum, we still need to skip the remaining accounts
|
|
|
|
|
+ // (`to_skip`) to move the input pointer to the instruction data. At the end, we return
|
|
|
|
|
+ // the number of accounts processed (`processed`), which represents the accounts
|
|
|
|
|
+ // initialized in the `accounts` slice.
|
|
|
//
|
|
//
|
|
|
- // Note that `to_process_plus_one` includes the first (already processed)
|
|
|
|
|
- // account to avoid decrementing the value. The actual number of remaining
|
|
|
|
|
- // accounts to process is `to_process_plus_one - 1`.
|
|
|
|
|
|
|
+ // Note that `to_process_plus_one` includes the first (already processed) account to
|
|
|
|
|
+ // avoid decrementing the value. The actual number of remaining accounts to process is
|
|
|
|
|
+ // `to_process_plus_one - 1`.
|
|
|
let mut to_process_plus_one = if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
|
|
let mut to_process_plus_one = if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
|
|
|
min(processed, MAX_ACCOUNTS)
|
|
min(processed, MAX_ACCOUNTS)
|
|
|
} else {
|
|
} else {
|
|
@@ -349,9 +347,9 @@ pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
|
let mut to_skip = processed - to_process_plus_one;
|
|
let mut to_skip = processed - to_process_plus_one;
|
|
|
processed = to_process_plus_one;
|
|
processed = to_process_plus_one;
|
|
|
|
|
|
|
|
- // This is an optimization to reduce the number of jumps required
|
|
|
|
|
- // to process the accounts. The macro `process_accounts` will generate
|
|
|
|
|
- // inline code to process the specified number of accounts.
|
|
|
|
|
|
|
+ // This is an optimization to reduce the number of jumps required to process the
|
|
|
|
|
+ // accounts. The macro `process_accounts` will generate inline code to process the
|
|
|
|
|
+ // specified number of accounts.
|
|
|
if to_process_plus_one == 2 {
|
|
if to_process_plus_one == 2 {
|
|
|
process_accounts!(1 => (input, accounts, accounts_slice));
|
|
process_accounts!(1 => (input, accounts, accounts_slice));
|
|
|
} else {
|
|
} else {
|
|
@@ -384,13 +382,13 @@ pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Process any remaining accounts to move the offset to the instruction
|
|
|
|
|
- // data (there is a duplication of logic but we avoid testing whether we
|
|
|
|
|
- // have space for the account or not).
|
|
|
|
|
|
|
+ // Process any remaining accounts to move the offset to the instruction data (there is a
|
|
|
|
|
+ // duplication of logic but we avoid testing whether we have space for the account or
|
|
|
|
|
+ // not).
|
|
|
//
|
|
//
|
|
|
- // There might be accounts to skip only when `MAX_ACCOUNTS < MAX_TX_ACCOUNTS`
|
|
|
|
|
- // so this allows the compiler to optimize the code and avoid the loop when
|
|
|
|
|
- // `MAX_ACCOUNTS == MAX_TX_ACCOUNTS`.
|
|
|
|
|
|
|
+ // There might be accounts to skip only when `MAX_ACCOUNTS < MAX_TX_ACCOUNTS` so this
|
|
|
|
|
+ // allows the compiler to optimize the code and avoid the loop when `MAX_ACCOUNTS ==
|
|
|
|
|
+ // MAX_TX_ACCOUNTS`.
|
|
|
if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
|
|
if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
|
|
|
while to_skip > 0 {
|
|
while to_skip > 0 {
|
|
|
// Marks the account as skipped.
|
|
// Marks the account as skipped.
|
|
@@ -428,9 +426,9 @@ pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
|
|
|
|
|
|
|
|
/// Default panic hook.
|
|
/// Default panic hook.
|
|
|
///
|
|
///
|
|
|
-/// This macro sets up a default panic hook that logs the panic message and the file where the
|
|
|
|
|
-/// panic occurred. It acts as a hook after Rust runtime panics; syscall `abort()` will be called
|
|
|
|
|
-/// after it returns.
|
|
|
|
|
|
|
+/// This macro sets up a default panic hook that logs the panic message and the file where the panic
|
|
|
|
|
+/// occurred. It acts as a hook after Rust runtime panics; syscall `abort()` will be called after it
|
|
|
|
|
+/// returns.
|
|
|
///
|
|
///
|
|
|
/// Note that this requires the `"std"` feature to be enabled.
|
|
/// Note that this requires the `"std"` feature to be enabled.
|
|
|
#[cfg(feature = "std")]
|
|
#[cfg(feature = "std")]
|
|
@@ -449,8 +447,8 @@ macro_rules! default_panic_handler {
|
|
|
|
|
|
|
|
/// Default panic hook.
|
|
/// Default panic hook.
|
|
|
///
|
|
///
|
|
|
-/// This macro sets up a default panic hook that logs the file where the panic occurred. It acts
|
|
|
|
|
-/// as a hook after Rust runtime panics; syscall `abort()` will be called after it returns.
|
|
|
|
|
|
|
+/// This macro sets up a default panic hook that logs the file where the panic occurred. It acts as
|
|
|
|
|
+/// a hook after Rust runtime panics; syscall `abort()` will be called after it returns.
|
|
|
///
|
|
///
|
|
|
/// This is used when the `"std"` feature is disabled, while either the program or any of its
|
|
/// This is used when the `"std"` feature is disabled, while either the program or any of its
|
|
|
/// dependencies are not `no_std`.
|
|
/// dependencies are not `no_std`.
|
|
@@ -473,11 +471,10 @@ macro_rules! default_panic_handler {
|
|
|
|
|
|
|
|
/// A global `#[panic_handler]` for `no_std` programs.
|
|
/// A global `#[panic_handler]` for `no_std` programs.
|
|
|
///
|
|
///
|
|
|
-/// This macro sets up a default panic handler that logs the location (file, line and column)
|
|
|
|
|
-/// where the panic occurred and then calls the syscall `abort()`.
|
|
|
|
|
|
|
+/// This macro sets up a default panic handler that logs the location (file, line and column) where
|
|
|
|
|
+/// the panic occurred and then calls the syscall `abort()`.
|
|
|
///
|
|
///
|
|
|
-/// This macro can only be used when all crates are `no_std` and the `"std"` feature is
|
|
|
|
|
-/// disabled.
|
|
|
|
|
|
|
+/// This macro can only be used when all crates are `no_std` and the `"std"` feature is disabled.
|
|
|
#[cfg(not(feature = "std"))]
|
|
#[cfg(not(feature = "std"))]
|
|
|
#[macro_export]
|
|
#[macro_export]
|
|
|
macro_rules! nostd_panic_handler {
|
|
macro_rules! nostd_panic_handler {
|
|
@@ -540,7 +537,7 @@ macro_rules! default_allocator {
|
|
|
|
|
|
|
|
/// A global allocator that does not allocate memory.
|
|
/// A global allocator that does not allocate memory.
|
|
|
///
|
|
///
|
|
|
-/// Using this macro with the "`std`" feature enabled will result in a compile error.
|
|
|
|
|
|
|
+/// Using this macro with the `"std"` feature enabled will result in a compile error.
|
|
|
#[cfg(feature = "std")]
|
|
#[cfg(feature = "std")]
|
|
|
#[macro_export]
|
|
#[macro_export]
|
|
|
macro_rules! no_allocator {
|
|
macro_rules! no_allocator {
|
|
@@ -551,9 +548,9 @@ macro_rules! no_allocator {
|
|
|
|
|
|
|
|
/// A global allocator that does not dynamically allocate memory.
|
|
/// A global allocator that does not dynamically allocate memory.
|
|
|
///
|
|
///
|
|
|
-/// This macro sets up a global allocator that denies all dynamic allocations, while
|
|
|
|
|
-/// allowing static ("manual") allocations. This is useful when the program does not need to
|
|
|
|
|
-/// dynamically allocate memory and manages their own allocations.
|
|
|
|
|
|
|
+/// This macro sets up a global allocator that denies all dynamic allocations, while allowing static
|
|
|
|
|
+/// ("manual") allocations. This is useful when the program does not need to dynamically allocate
|
|
|
|
|
+/// memory and manages their own allocations.
|
|
|
///
|
|
///
|
|
|
/// The program will panic if it tries to dynamically allocate memory.
|
|
/// The program will panic if it tries to dynamically allocate memory.
|
|
|
///
|
|
///
|
|
@@ -566,21 +563,19 @@ macro_rules! no_allocator {
|
|
|
#[global_allocator]
|
|
#[global_allocator]
|
|
|
static A: $crate::entrypoint::NoAllocator = $crate::entrypoint::NoAllocator;
|
|
static A: $crate::entrypoint::NoAllocator = $crate::entrypoint::NoAllocator;
|
|
|
|
|
|
|
|
- /// Allocates memory for the given type `T` at the specified offset in the
|
|
|
|
|
- /// heap reserved address space.
|
|
|
|
|
|
|
+ /// Allocates memory for the given type `T` at the specified offset in the heap reserved
|
|
|
|
|
+ /// address space.
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
- /// It is the caller's responsibility to ensure that the offset does not
|
|
|
|
|
- /// overlap with previous allocations and that type `T` can hold the bit-pattern
|
|
|
|
|
- /// `0` as a valid value.
|
|
|
|
|
|
|
+ /// It is the caller's responsibility to ensure that the offset does not overlap with
|
|
|
|
|
+ /// previous allocations and that type `T` can hold the bit-pattern `0` as a valid value.
|
|
|
///
|
|
///
|
|
|
/// For types that cannot hold the bit-pattern `0` as a valid value, use
|
|
/// For types that cannot hold the bit-pattern `0` as a valid value, use
|
|
|
- /// `core::mem::MaybeUninit<T>` to allocate memory for the type and
|
|
|
|
|
- /// initialize it later.
|
|
|
|
|
|
|
+ /// [`core::mem::MaybeUninit<T>`] to allocate memory for the type and initialize it later.
|
|
|
//
|
|
//
|
|
|
- // Make this `const` once `const_mut_refs` is stable for the platform-tools
|
|
|
|
|
- // toolchain Rust version.
|
|
|
|
|
|
|
+ // Make this `const` once `const_mut_refs` is stable for the platform-tools toolchain Rust
|
|
|
|
|
+ // version.
|
|
|
#[inline(always)]
|
|
#[inline(always)]
|
|
|
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut T {
|
|
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut T {
|
|
|
// SAFETY: The pointer is within a valid range and aligned to `T`.
|
|
// SAFETY: The pointer is within a valid range and aligned to `T`.
|
|
@@ -631,9 +626,9 @@ mod alloc {
|
|
|
pub len: usize,
|
|
pub len: usize,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Integer arithmetic in this global allocator implementation is safe when
|
|
|
|
|
- /// operating on the prescribed [`HEAP_START_ADDRESS`] and [`HEAP_LENGTH`]. Any
|
|
|
|
|
- /// other use may overflow and is thus unsupported and at one's own risk.
|
|
|
|
|
|
|
+ /// Integer arithmetic in this global allocator implementation is safe when operating on the
|
|
|
|
|
+ /// prescribed [`HEAP_START_ADDRESS`] and [`HEAP_LENGTH`]. Any other use may overflow and is
|
|
|
|
|
+ /// thus unsupported and at one's own risk.
|
|
|
#[allow(clippy::arithmetic_side_effects)]
|
|
#[allow(clippy::arithmetic_side_effects)]
|
|
|
unsafe impl alloc::alloc::GlobalAlloc for BumpAllocator {
|
|
unsafe impl alloc::alloc::GlobalAlloc for BumpAllocator {
|
|
|
/// Allocates memory as a bump allocator.
|
|
/// Allocates memory as a bump allocator.
|
|
@@ -654,6 +649,7 @@ mod alloc {
|
|
|
*pos_ptr = pos;
|
|
*pos_ptr = pos;
|
|
|
pos as *mut u8
|
|
pos as *mut u8
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
#[inline]
|
|
#[inline]
|
|
|
unsafe fn dealloc(&self, _: *mut u8, _: core::alloc::Layout) {
|
|
unsafe fn dealloc(&self, _: *mut u8, _: core::alloc::Layout) {
|
|
|
// I'm a bump allocator, I don't free.
|
|
// I'm a bump allocator, I don't free.
|
|
@@ -719,8 +715,8 @@ mod tests {
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
- /// The caller must ensure that the `data` length does not exceed the
|
|
|
|
|
- /// remaining space in the memory region starting from the `offset`.
|
|
|
|
|
|
|
+ /// The caller must ensure that the `data` length does not exceed the remaining space in the
|
|
|
|
|
+ /// memory region starting from the `offset`.
|
|
|
pub unsafe fn write(&mut self, data: &[u8], offset: usize) {
|
|
pub unsafe fn write(&mut self, data: &[u8], offset: usize) {
|
|
|
copy_nonoverlapping(data.as_ptr(), self.ptr.add(offset), data.len());
|
|
copy_nonoverlapping(data.as_ptr(), self.ptr.add(offset), data.len());
|
|
|
}
|
|
}
|
|
@@ -739,17 +735,14 @@ mod tests {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Creates an input buffer with a specified number of accounts and
|
|
|
|
|
- /// instruction data.
|
|
|
|
|
|
|
+ /// Creates an input buffer with a specified number of accounts and instruction data.
|
|
|
///
|
|
///
|
|
|
- /// This function mimics the input buffer created by the SVM loader.
|
|
|
|
|
- /// Each account created has zeroed data, apart from the `data_len`
|
|
|
|
|
- /// field, which is set to the index of the account.
|
|
|
|
|
|
|
+ /// This function mimics the input buffer created by the SVM loader. Each account created has
|
|
|
|
|
+ /// zeroed data, apart from the `data_len` field, which is set to the index of the account.
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
- /// The returned `AlignedMemory` should only be used within the test
|
|
|
|
|
- /// context.
|
|
|
|
|
|
|
+ /// The returned `AlignedMemory` should only be used within the test context.
|
|
|
unsafe fn create_input(accounts: usize, instruction_data: &[u8]) -> AlignedMemory {
|
|
unsafe fn create_input(accounts: usize, instruction_data: &[u8]) -> AlignedMemory {
|
|
|
let mut input = AlignedMemory::new(1_000_000_000);
|
|
let mut input = AlignedMemory::new(1_000_000_000);
|
|
|
// Number of accounts.
|
|
// Number of accounts.
|
|
@@ -782,22 +775,19 @@ mod tests {
|
|
|
input
|
|
input
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Creates an input buffer with a specified number of accounts, including
|
|
|
|
|
- /// duplicated accounts, and instruction data.
|
|
|
|
|
|
|
+ /// Creates an input buffer with a specified number of accounts, including duplicated accounts,
|
|
|
|
|
+ /// and instruction data.
|
|
|
///
|
|
///
|
|
|
- /// This function differs from `create_input` in that it creates accounts
|
|
|
|
|
- /// with a marker indicating that they are duplicated. There will be
|
|
|
|
|
- /// `accounts - duplicated` unique accounts, and the remaining `duplicated`
|
|
|
|
|
- /// accounts will be duplicates of the last unique account.
|
|
|
|
|
|
|
+ /// This function differs from `create_input` in that it creates accounts with a marker
|
|
|
|
|
+ /// indicating that they are duplicated. There will be `accounts - duplicated` unique accounts,
|
|
|
|
|
+ /// and the remaining `duplicated` accounts will be duplicates of the last unique account.
|
|
|
///
|
|
///
|
|
|
- /// This function mimics the input buffer created by the SVM loader.
|
|
|
|
|
- /// Each account created has zeroed data, apart from the `data_len`
|
|
|
|
|
- /// field, which is set to the index of the account.
|
|
|
|
|
|
|
+ /// This function mimics the input buffer created by the SVM loader. Each account created has
|
|
|
|
|
+ /// zeroed data, apart from the `data_len` field, which is set to the index of the account.
|
|
|
///
|
|
///
|
|
|
/// # Safety
|
|
/// # Safety
|
|
|
///
|
|
///
|
|
|
- /// The returned `AlignedMemory` should only be used within the test
|
|
|
|
|
- /// context.
|
|
|
|
|
|
|
+ /// The returned `AlignedMemory` should only be used within the test context.
|
|
|
unsafe fn create_input_with_duplicates(
|
|
unsafe fn create_input_with_duplicates(
|
|
|
accounts: usize,
|
|
accounts: usize,
|
|
|
instruction_data: &[u8],
|
|
instruction_data: &[u8],
|
|
@@ -848,8 +838,8 @@ mod tests {
|
|
|
input
|
|
input
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Asserts that the accounts slice contains the expected number of accounts
|
|
|
|
|
- /// and that each account's data length matches its index.
|
|
|
|
|
|
|
+ /// Asserts that the accounts slice contains the expected number of accounts and that each
|
|
|
|
|
+ /// account's data length matches its index.
|
|
|
fn assert_accounts(accounts: &[MaybeUninit<AccountInfo>]) {
|
|
fn assert_accounts(accounts: &[MaybeUninit<AccountInfo>]) {
|
|
|
for (i, account) in accounts.iter().enumerate() {
|
|
for (i, account) in accounts.iter().enumerate() {
|
|
|
let account_info = unsafe { account.assume_init_ref() };
|
|
let account_info = unsafe { account.assume_init_ref() };
|
|
@@ -857,8 +847,8 @@ mod tests {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Asserts that the accounts slice contains the expected number of accounts
|
|
|
|
|
- /// and all accounts are duplicated, apart from the first one.
|
|
|
|
|
|
|
+ /// Asserts that the accounts slice contains the expected number of accounts and all accounts
|
|
|
|
|
+ /// are duplicated, apart from the first one.
|
|
|
fn assert_duplicated_accounts(accounts: &[MaybeUninit<AccountInfo>], duplicated: usize) {
|
|
fn assert_duplicated_accounts(accounts: &[MaybeUninit<AccountInfo>], duplicated: usize) {
|
|
|
assert!(accounts.len() > duplicated);
|
|
assert!(accounts.len() > duplicated);
|
|
|
|
|
|
|
@@ -956,9 +946,9 @@ mod tests {
|
|
|
assert_eq!(program_id, &MOCK_PROGRAM_ID);
|
|
assert_eq!(program_id, &MOCK_PROGRAM_ID);
|
|
|
assert_eq!(&ix_data, parsed_ix_data);
|
|
assert_eq!(&ix_data, parsed_ix_data);
|
|
|
|
|
|
|
|
- // Input with 3 (1 + 2 duplicated) accounts but the accounts array has only
|
|
|
|
|
- // space for 2. The assert checks that the second account is a duplicate of
|
|
|
|
|
- // the first one and the first one is unique.
|
|
|
|
|
|
|
+ // Input with 3 (1 + 2 duplicated) accounts but the accounts array has only space for 2. The
|
|
|
|
|
+ // assert checks that the second account is a duplicate of the first one and the first one
|
|
|
|
|
+ // is unique.
|
|
|
|
|
|
|
|
let mut input = unsafe { create_input_with_duplicates(3, &ix_data, 2) };
|
|
let mut input = unsafe { create_input_with_duplicates(3, &ix_data, 2) };
|
|
|
let mut accounts = [UNINIT; 2];
|
|
let mut accounts = [UNINIT; 2];
|
|
@@ -971,9 +961,9 @@ mod tests {
|
|
|
assert_eq!(&ix_data, parsed_ix_data);
|
|
assert_eq!(&ix_data, parsed_ix_data);
|
|
|
assert_duplicated_accounts(&accounts[..count], 1);
|
|
assert_duplicated_accounts(&accounts[..count], 1);
|
|
|
|
|
|
|
|
- // Input with `MAX_TX_ACCOUNTS` accounts (only 32 unique ones) but accounts
|
|
|
|
|
- // array has only space for 64. The assert checks that the first 32 accounts
|
|
|
|
|
- // are unique and the rest are duplicates of the account at index 31.
|
|
|
|
|
|
|
+ // Input with `MAX_TX_ACCOUNTS` accounts (only 32 unique ones) but accounts array has only
|
|
|
|
|
+ // space for 64. The assert checks that the first 32 accounts are unique and the rest are
|
|
|
|
|
+ // duplicates of the account at index 31.
|
|
|
|
|
|
|
|
let mut input = unsafe {
|
|
let mut input = unsafe {
|
|
|
create_input_with_duplicates(MAX_TX_ACCOUNTS, &ix_data, MAX_TX_ACCOUNTS - 32)
|
|
create_input_with_duplicates(MAX_TX_ACCOUNTS, &ix_data, MAX_TX_ACCOUNTS - 32)
|