Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rust-version = "1.84"

[workspace.dependencies]
five8_const = "0.1.4"
pinocchio = { version = "0.9", path = "sdk/pinocchio" }
pinocchio = { version = "0.9", default-features = false, path = "sdk/pinocchio" }
pinocchio-log-macro = { version = "0.5", path = "sdk/log/macro" }
quote = "1.0"
regex = "1"
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,19 @@ pub fn process_instruction(
> ⚠️ **Note:**
> The `no_allocator!` macro can also be used in combination with the `lazy_program_entrypoint!`.

## Crate feature: `std`
## Crate feature: `alloc`

The `alloc` feature is enabled by default and it uses the [`alloc`](https://doc.rust-lang.org/alloc/) crate. This provides access to dynamic memory allocation in combination with the `default_allocator`, e.g., required to use `String` and `Vec` in a program. Helpers that need to allocate memory, such as fetching `SlotHashes` sysvar data, are also available.

When no allocation is needed or desired, the feature can be disabled:

By default, Pinocchio is a `no_std` crate. This means that it does not use any code from the
standard (`std`) library. While this does not affect how Pinocchio is used, there is a one
particular apparent difference. Helpers that need to allocate memory, such as fetching `SlotHashes`
sysvar data, are not available. To enable these helpers, the `std` feature must be enabled when adding
Pinocchio as a dependency:
```
pinocchio = { version = "0.10.0", features = ["std"] }
pinocchio = { version = "0.10.0", default-features = false }
```

> ⚠️ **Note:**
> The `default_allocator` macro is not available when disabling the `alloc` feature.

## Advance entrypoint configuration

The symbols emitted by the entrypoint macros — program entrypoint, global allocator and default panic handler — can only be defined once globally. If the program crate is also intended to be used as a library, it is common practice to define a Cargo [feature](https://doc.rust-lang.org/cargo/reference/features.html) in your program crate to conditionally enable the module that includes the `entrypoint!` macro invocation. The convention is to name the feature `bpf-entrypoint`.
Expand Down
3 changes: 2 additions & 1 deletion sdk/pinocchio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ unexpected_cfgs = { level = "warn", check-cfg = [
] }

[features]
alloc = []
copy = ["solana-account-view/copy", "solana-address/copy"]
cpi = ["dep:solana-instruction-view"]
std = ["solana-address/std"]
default = ["alloc"]

[dependencies]
solana-account-view = { workspace = true }
Expand Down
79 changes: 30 additions & 49 deletions sdk/pinocchio/src/entrypoint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ pub mod lazy;

pub use lazy::{InstructionContext, MaybeAccount};

#[cfg(not(feature = "std"))]
use core::alloc::{GlobalAlloc, Layout};

#[cfg(target_os = "solana")]
pub use alloc::BumpAllocator;
use core::{
alloc::{GlobalAlloc, Layout},
cmp::min,
mem::{size_of, MaybeUninit},
ptr::with_exposed_provenance_mut,
Expand All @@ -22,6 +18,9 @@ use crate::{
Address, BPF_ALIGN_OF_U128, MAX_TX_ACCOUNTS,
};

#[cfg(all(target_os = "solana", feature = "alloc"))]
pub use alloc::BumpAllocator;

/// Start address of the memory region used for program heap.
pub const HEAP_START_ADDRESS: u64 = 0x300000000;

Expand Down Expand Up @@ -125,6 +124,7 @@ const STATIC_ACCOUNT_DATA: usize = size_of::<Account>() + MAX_PERMITTED_DATA_INC
/// manually.
///
/// [`crate::nostd_panic_handler`]: https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html
#[cfg(feature = "alloc")]
#[macro_export]
macro_rules! entrypoint {
( $process_instruction:expr ) => {
Expand Down Expand Up @@ -279,9 +279,9 @@ macro_rules! process_accounts {
//
// 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.
#[allow(clippy::clone_on_copy)]
#[cold]
#[inline(always)]
#[allow(clippy::clone_on_copy)]
unsafe fn clone_account_info(
accounts: *mut AccountView,
accounts_slice: *const AccountView,
Expand Down Expand Up @@ -459,14 +459,12 @@ macro_rules! default_panic_handler {
/// 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.
#[cfg(not(feature = "std"))]
/// This macro should be used when all crates are `no_std`.
#[macro_export]
macro_rules! nostd_panic_handler {
() => {
/// A panic handler for `no_std`.
#[cfg(target_os = "solana")]
#[no_mangle]
#[panic_handler]
fn handler(info: &core::panic::PanicInfo<'_>) -> ! {
if let Some(location) = info.location() {
Expand Down Expand Up @@ -502,6 +500,7 @@ macro_rules! nostd_panic_handler {
/// Default global allocator.
///
/// This macro sets up a default global allocator that uses a bump allocator to allocate memory.
#[cfg(feature = "alloc")]
#[macro_export]
macro_rules! default_allocator {
() => {
Expand All @@ -523,17 +522,6 @@ macro_rules! default_allocator {
};
}

/// A global allocator that does not allocate memory.
///
/// Using this macro with the `"std"` feature enabled will result in a compile error.
#[cfg(feature = "std")]
#[macro_export]
macro_rules! no_allocator {
() => {
compile_error!("Feature 'std' cannot be enabled.");
};
}

/// A global allocator that does not dynamically allocate memory.
///
/// This macro sets up a global allocator that denies all dynamic allocations, while allowing static
Expand All @@ -542,8 +530,7 @@ macro_rules! no_allocator {
///
/// The program will panic if it tries to dynamically allocate memory.
///
/// This is used when the `"std"` feature is disabled.
#[cfg(not(feature = "std"))]
/// This is used when the `"alloc"` feature is disabled.
#[macro_export]
macro_rules! no_allocator {
() => {
Expand Down Expand Up @@ -602,12 +589,25 @@ macro_rules! no_allocator {
};
}

#[cfg(target_os = "solana")]
mod alloc {
//! The bump allocator used as the default rust heap when running programs.
/// An allocator that does not allocate memory.
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone, Debug)]
pub struct NoAllocator;

extern crate alloc;
unsafe impl GlobalAlloc for NoAllocator {
#[inline]
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
panic!("** NoAllocator::alloc() does not allocate memory **");
}

#[inline]
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
// I deny all allocations, so I don't need to free.
}
}

#[cfg(all(target_os = "solana", feature = "alloc"))]
mod alloc {
use core::{
alloc::{GlobalAlloc, Layout},
mem::size_of,
Expand Down Expand Up @@ -653,34 +653,15 @@ mod alloc {
}
}

#[cfg(not(feature = "std"))]
/// An allocator that does not allocate memory.
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone, Debug)]
pub struct NoAllocator;

#[cfg(not(feature = "std"))]
unsafe impl GlobalAlloc for NoAllocator {
#[inline]
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
panic!("** NO ALLOCATOR **");
}

#[inline]
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
// I deny all allocations, so I don't need to free.
}
}

#[cfg(all(test, not(target_os = "solana")))]
mod tests {
extern crate std;
extern crate alloc;

use core::{alloc::Layout, ptr::copy_nonoverlapping};
use std::{
use alloc::{
alloc::{alloc, dealloc},
vec,
};
use core::{alloc::Layout, ptr::copy_nonoverlapping};

use super::*;

Expand All @@ -703,7 +684,7 @@ mod tests {
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
alloc::alloc::handle_alloc_error(layout);
}
AlignedMemory { ptr, layout }
}
Expand Down
10 changes: 5 additions & 5 deletions sdk/pinocchio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@
//! code from the standard (`std`) library. While this does not affect how Pinocchio
//! is used, there is a one particular apparent difference. Helpers that need to
//! allocate memory, such as fetching `SlotHashes` sysvar, are not available. To
//! enable these helpers, the `std` feature must be enabled when adding Pinocchio
//! enable these helpers, the `alloc` feature must be enabled when adding Pinocchio
//! as a dependency:
//! ```ignore
//! pinocchio = { version = "0.10.0", features = ["std"] }
//! pinocchio = { version = "0.10.0", features = ["alloc"] }
//! ```
//!
//! ## Advanced entrypoint configuration
Expand Down Expand Up @@ -208,10 +208,10 @@
//! cargo build-sbf --features bpf-entrypoint
//! ```

#![no_std]
#![cfg_attr(not(test), no_std)]

#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;

pub mod entrypoint;
pub mod sysvars;
Expand Down
8 changes: 4 additions & 4 deletions sdk/pinocchio/src/sysvars/slot_hashes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use crate::{
Address,
};

#[cfg(feature = "std")]
use std::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, vec::Vec};

/// `SysvarS1otHashes111111111111111111111111111`
pub const SLOTHASHES_ID: Address = Address::new_from_array([
Expand Down Expand Up @@ -299,7 +299,7 @@ impl<'a> SlotHashes<Ref<'a, [u8]>> {
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl SlotHashes<Box<[u8]>> {
/// Fills the provided buffer with the full `SlotHashes` sysvar data.
///
Expand All @@ -320,7 +320,7 @@ impl SlotHashes<Box<[u8]>> {
/// Allocates an optimal buffer for the sysvar data based on available features.
#[inline(always)]
fn allocate_and_fetch() -> Result<Box<[u8]>, ProgramError> {
let mut buf = std::vec::Vec::with_capacity(MAX_SIZE);
let mut buf = Vec::with_capacity(MAX_SIZE);
unsafe {
// SAFETY: `buf` was allocated with capacity `MAX_SIZE` so its
// pointer is valid for exactly that many bytes. `fill_from_sysvar`
Expand Down
3 changes: 1 addition & 2 deletions sdk/pinocchio/src/sysvars/slot_hashes/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use core::{
ptr,
};

extern crate std;
use std::io::Write;
use std::vec::Vec;

Expand Down Expand Up @@ -460,7 +459,7 @@ fn test_from_account_info_constructor() {
/// `SlotHashes` getters to make sure the view itself works. We do not verify
/// that the syscall populated real on-chain bytes, as doing so requires an
/// environment outside the scope of host `cargo test`.
#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
#[test]
fn test_fetch_allocates_buffer_host() {
const START_SLOT: u64 = 500;
Expand Down
3 changes: 1 addition & 2 deletions sdk/pinocchio/src/sysvars/slot_hashes/test_edge.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{error::ProgramError, sysvars::slot_hashes::*};
extern crate std;
use super::test_utils::{build_slot_hashes_bytes as raw_slot_hashes, make_account_info};
use crate::{error::ProgramError, sysvars::slot_hashes::*};

#[test]
fn test_wrong_key_from_account_view() {
Expand Down
1 change: 0 additions & 1 deletion sdk/pinocchio/src/sysvars/slot_hashes/test_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use super::raw;
use super::*;
extern crate std;

#[test]
fn test_validate_buffer_size() {
Expand Down
1 change: 0 additions & 1 deletion sdk/pinocchio/src/sysvars/slot_hashes/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
//! freely while production code remains `#![no_std]`.
use super::*;
extern crate std;
use crate::account::{Account, AccountView};
use core::ptr;
use std::vec::Vec;
Expand Down