Skip to main content

hopper_native/
mem.rs

1//! SVM-optimized memory operations.
2//!
3//! The Solana BPF VM provides syscall-level memory operations that are
4//! faster than Rust's default libc implementations because they are
5//! JIT-compiled intrinsics in the VM. No framework exposes these at the
6//! substrate level.
7//!
8//! On BPF, these dispatch to `sol_memcpy_`, `sol_memmove_`, `sol_memcmp_`,
9//! and `sol_memset_`. Off-chain, they fall back to standard library
10//! implementations.
11//!
12//! Use these instead of `core::ptr::copy_nonoverlapping`, `ptr::write_bytes`,
13//! etc. for best performance on the SVM.
14
15use crate::error::ProgramError;
16
17/// Copy `n` bytes from `src` to `dst`.
18///
19/// The memory regions **must not overlap**. For overlapping copies, use
20/// `memmove`. This is enforced by the SVM runtime on BPF.
21///
22/// # Safety
23///
24/// Both `src` and `dst` must be valid for `n` bytes. Regions must not overlap.
25#[inline(always)]
26pub unsafe fn memcpy(dst: *mut u8, src: *const u8, n: usize) {
27    #[cfg(target_os = "solana")]
28    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
29    unsafe {
30        crate::syscalls::sol_memcpy_(dst, src, n as u64);
31    }
32    #[cfg(not(target_os = "solana"))]
33    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
34    unsafe {
35        core::ptr::copy_nonoverlapping(src, dst, n);
36    }
37}
38
39/// Copy `n` bytes from `src` to `dst`, handling overlapping regions.
40///
41/// Safe for any src/dst alignment and overlap pattern.
42///
43/// # Safety
44///
45/// Both `src` and `dst` must be valid for `n` bytes.
46#[inline(always)]
47pub unsafe fn memmove(dst: *mut u8, src: *const u8, n: usize) {
48    #[cfg(target_os = "solana")]
49    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
50    unsafe {
51        crate::syscalls::sol_memmove_(dst, src, n as u64);
52    }
53    #[cfg(not(target_os = "solana"))]
54    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
55    unsafe {
56        core::ptr::copy(src, dst, n);
57    }
58}
59
60/// Fill `n` bytes starting at `dst` with `byte`.
61///
62/// The fastest way to zero-fill or pattern-fill a memory region on the SVM.
63///
64/// # Safety
65///
66/// `dst` must be valid for `n` bytes.
67#[inline(always)]
68pub unsafe fn memset(dst: *mut u8, byte: u8, n: usize) {
69    #[cfg(target_os = "solana")]
70    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
71    unsafe {
72        crate::syscalls::sol_memset_(dst, byte, n as u64);
73    }
74    #[cfg(not(target_os = "solana"))]
75    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
76    unsafe {
77        core::ptr::write_bytes(dst, byte, n);
78    }
79}
80
81/// Compare `n` bytes between two memory regions.
82///
83/// Returns `Ordering::Equal` if the regions are identical, or the
84/// ordering of the first differing byte (lexicographic comparison).
85///
86/// # Safety
87///
88/// Both `a` and `b` must be valid for `n` bytes.
89#[inline(always)]
90pub unsafe fn memcmp(a: *const u8, b: *const u8, n: usize) -> core::cmp::Ordering {
91    #[cfg(target_os = "solana")]
92    {
93        let mut result: i32 = 0;
94        // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
95        unsafe {
96            crate::syscalls::sol_memcmp_(a, b, n as u64, &mut result as *mut i32);
97        }
98        match result {
99            0 => core::cmp::Ordering::Equal,
100            x if x < 0 => core::cmp::Ordering::Less,
101            _ => core::cmp::Ordering::Greater,
102        }
103    }
104    #[cfg(not(target_os = "solana"))]
105    {
106        // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
107        let a_slice = unsafe { core::slice::from_raw_parts(a, n) };
108        let b_slice = unsafe { core::slice::from_raw_parts(b, n) };
109        a_slice.cmp(b_slice)
110    }
111}
112
113// ---- Safe wrappers ---------------------------------------------------
114
115/// Zero-fill a mutable byte slice using the SVM-optimized memset.
116#[inline(always)]
117pub fn zero_fill(buf: &mut [u8]) {
118    if buf.is_empty() {
119        return;
120    }
121    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
122    unsafe {
123        memset(buf.as_mut_ptr(), 0, buf.len());
124    }
125}
126
127/// Copy bytes from one slice to another (no overlap).
128///
129/// Returns `Err(InvalidArgument)` if lengths differ.
130#[inline]
131pub fn copy_bytes(dst: &mut [u8], src: &[u8]) -> Result<(), ProgramError> {
132    if dst.len() < src.len() {
133        return Err(ProgramError::InvalidArgument);
134    }
135    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
136    unsafe {
137        memcpy(dst.as_mut_ptr(), src.as_ptr(), src.len());
138    }
139    Ok(())
140}
141
142/// Compare two byte slices for equality using SVM-optimized memcmp.
143#[inline]
144pub fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
145    if a.len() != b.len() {
146        return false;
147    }
148    if a.is_empty() {
149        return true;
150    }
151    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
152    unsafe { memcmp(a.as_ptr(), b.as_ptr(), a.len()) == core::cmp::Ordering::Equal }
153}
154
155/// Zero-fill account data using SVM-optimized memset.
156///
157/// More efficient than the byte-by-byte loop in `AccountView::close()`.
158/// Use this when you need to clear account data without closing the account.
159#[inline]
160pub fn zero_account_data(account: &crate::account_view::AccountView) {
161    let len = account.data_len();
162    if len == 0 {
163        return;
164    }
165    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
166    unsafe {
167        memset(account.data_ptr_unchecked(), 0, len);
168    }
169}