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 unsafe {
29 crate::syscalls::sol_memcpy_(dst, src, n as u64);
30 }
31 #[cfg(not(target_os = "solana"))]
32 unsafe {
33 core::ptr::copy_nonoverlapping(src, dst, n);
34 }
35}
36
37/// Copy `n` bytes from `src` to `dst`, handling overlapping regions.
38///
39/// Safe for any src/dst alignment and overlap pattern.
40///
41/// # Safety
42///
43/// Both `src` and `dst` must be valid for `n` bytes.
44#[inline(always)]
45pub unsafe fn memmove(dst: *mut u8, src: *const u8, n: usize) {
46 #[cfg(target_os = "solana")]
47 unsafe {
48 crate::syscalls::sol_memmove_(dst, src, n as u64);
49 }
50 #[cfg(not(target_os = "solana"))]
51 unsafe {
52 core::ptr::copy(src, dst, n);
53 }
54}
55
56/// Fill `n` bytes starting at `dst` with `byte`.
57///
58/// The fastest way to zero-fill or pattern-fill a memory region on the SVM.
59///
60/// # Safety
61///
62/// `dst` must be valid for `n` bytes.
63#[inline(always)]
64pub unsafe fn memset(dst: *mut u8, byte: u8, n: usize) {
65 #[cfg(target_os = "solana")]
66 unsafe {
67 crate::syscalls::sol_memset_(dst, byte, n as u64);
68 }
69 #[cfg(not(target_os = "solana"))]
70 unsafe {
71 core::ptr::write_bytes(dst, byte, n);
72 }
73}
74
75/// Compare `n` bytes between two memory regions.
76///
77/// Returns `Ordering::Equal` if the regions are identical, or the
78/// ordering of the first differing byte (lexicographic comparison).
79///
80/// # Safety
81///
82/// Both `a` and `b` must be valid for `n` bytes.
83#[inline(always)]
84pub unsafe fn memcmp(a: *const u8, b: *const u8, n: usize) -> core::cmp::Ordering {
85 #[cfg(target_os = "solana")]
86 {
87 let mut result: i32 = 0;
88 unsafe {
89 crate::syscalls::sol_memcmp_(a, b, n as u64, &mut result as *mut i32);
90 }
91 match result {
92 0 => core::cmp::Ordering::Equal,
93 x if x < 0 => core::cmp::Ordering::Less,
94 _ => core::cmp::Ordering::Greater,
95 }
96 }
97 #[cfg(not(target_os = "solana"))]
98 {
99 let a_slice = unsafe { core::slice::from_raw_parts(a, n) };
100 let b_slice = unsafe { core::slice::from_raw_parts(b, n) };
101 a_slice.cmp(b_slice)
102 }
103}
104
105// ---- Safe wrappers ---------------------------------------------------
106
107/// Zero-fill a mutable byte slice using the SVM-optimized memset.
108#[inline(always)]
109pub fn zero_fill(buf: &mut [u8]) {
110 if buf.is_empty() {
111 return;
112 }
113 unsafe {
114 memset(buf.as_mut_ptr(), 0, buf.len());
115 }
116}
117
118/// Copy bytes from one slice to another (no overlap).
119///
120/// Returns `Err(InvalidArgument)` if lengths differ.
121#[inline]
122pub fn copy_bytes(dst: &mut [u8], src: &[u8]) -> Result<(), ProgramError> {
123 if dst.len() < src.len() {
124 return Err(ProgramError::InvalidArgument);
125 }
126 unsafe {
127 memcpy(dst.as_mut_ptr(), src.as_ptr(), src.len());
128 }
129 Ok(())
130}
131
132/// Compare two byte slices for equality using SVM-optimized memcmp.
133#[inline]
134pub fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
135 if a.len() != b.len() {
136 return false;
137 }
138 if a.is_empty() {
139 return true;
140 }
141 unsafe { memcmp(a.as_ptr(), b.as_ptr(), a.len()) == core::cmp::Ordering::Equal }
142}
143
144/// Zero-fill account data using SVM-optimized memset.
145///
146/// More efficient than the byte-by-byte loop in `AccountView::close()`.
147/// Use this when you need to clear account data without closing the account.
148#[inline]
149pub fn zero_account_data(account: &crate::account_view::AccountView) {
150 let len = account.data_len();
151 if len == 0 {
152 return;
153 }
154 unsafe {
155 memset(account.data_ptr_unchecked(), 0, len);
156 }
157}