Skip to main content

hopper_native/
mem.rs

1//! Memory operations backed by Solana SVM syscalls.
2//!
3//! On Solana SBF, these dispatch to `sol_memcpy_`, `sol_memmove_`, `sol_memcmp_`,
4//! and `sol_memset_`. Off-chain, they fall back to standard library
5//! implementations.
6
7use crate::error::ProgramError;
8
9/// Copy `n` bytes from `src` to `dst`.
10///
11/// The memory regions **must not overlap**. For overlapping copies, use
12/// `memmove`. This is enforced by the SVM runtime on BPF.
13///
14/// # Safety
15///
16/// Both `src` and `dst` must be valid for `n` bytes. Regions must not overlap.
17#[inline(always)]
18pub unsafe fn memcpy(dst: *mut u8, src: *const u8, n: usize) {
19    #[cfg(target_os = "solana")]
20    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
21    unsafe {
22        crate::syscalls::sol_memcpy_(dst, src, n as u64);
23    }
24    #[cfg(not(target_os = "solana"))]
25    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
26    unsafe {
27        core::ptr::copy_nonoverlapping(src, dst, n);
28    }
29}
30
31/// Copy `n` bytes from `src` to `dst`, handling overlapping regions.
32///
33/// Safe for any src/dst alignment and overlap pattern.
34///
35/// # Safety
36///
37/// Both `src` and `dst` must be valid for `n` bytes.
38#[inline(always)]
39pub unsafe fn memmove(dst: *mut u8, src: *const u8, n: usize) {
40    #[cfg(target_os = "solana")]
41    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
42    unsafe {
43        crate::syscalls::sol_memmove_(dst, src, n as u64);
44    }
45    #[cfg(not(target_os = "solana"))]
46    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
47    unsafe {
48        core::ptr::copy(src, dst, n);
49    }
50}
51
52/// Fill `n` bytes starting at `dst` with `byte`.
53///
54/// Uses the SVM `sol_memset_` syscall on-chain.
55///
56/// # Safety
57///
58/// `dst` must be valid for `n` bytes.
59#[inline(always)]
60pub unsafe fn memset(dst: *mut u8, byte: u8, n: usize) {
61    #[cfg(target_os = "solana")]
62    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
63    unsafe {
64        crate::syscalls::sol_memset_(dst, byte, n as u64);
65    }
66    #[cfg(not(target_os = "solana"))]
67    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
68    unsafe {
69        core::ptr::write_bytes(dst, byte, n);
70    }
71}
72
73/// Compare `n` bytes between two memory regions.
74///
75/// Returns `Ordering::Equal` if the regions are identical, or the
76/// ordering of the first differing byte (lexicographic comparison).
77///
78/// # Safety
79///
80/// Both `a` and `b` must be valid for `n` bytes.
81#[inline(always)]
82pub unsafe fn memcmp(a: *const u8, b: *const u8, n: usize) -> core::cmp::Ordering {
83    #[cfg(target_os = "solana")]
84    {
85        let mut result: i32 = 0;
86        // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
87        unsafe {
88            crate::syscalls::sol_memcmp_(a, b, n as u64, &mut result as *mut i32);
89        }
90        match result {
91            0 => core::cmp::Ordering::Equal,
92            x if x < 0 => core::cmp::Ordering::Less,
93            _ => core::cmp::Ordering::Greater,
94        }
95    }
96    #[cfg(not(target_os = "solana"))]
97    {
98        // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
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    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
114    unsafe {
115        memset(buf.as_mut_ptr(), 0, buf.len());
116    }
117}
118
119/// Copy bytes from one slice to another (no overlap).
120///
121/// Copies `src.len()` bytes into the front of `dst`. Returns
122/// `Err(InvalidArgument)` if `dst` is shorter than `src`; a longer `dst`
123/// keeps its trailing bytes unchanged.
124#[inline]
125pub fn copy_bytes(dst: &mut [u8], src: &[u8]) -> Result<(), ProgramError> {
126    if dst.len() < src.len() {
127        return Err(ProgramError::InvalidArgument);
128    }
129    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
130    unsafe {
131        memcpy(dst.as_mut_ptr(), src.as_ptr(), src.len());
132    }
133    Ok(())
134}
135
136/// Compare two byte slices for equality using SVM-optimized memcmp.
137#[inline]
138pub fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
139    if a.len() != b.len() {
140        return false;
141    }
142    if a.is_empty() {
143        return true;
144    }
145    // SAFETY: This block is part of Hopper's reviewed zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
146    unsafe { memcmp(a.as_ptr(), b.as_ptr(), a.len()) == core::cmp::Ordering::Equal }
147}
148
149/// Zero-fill account data using SVM-optimized memset.
150///
151/// Use this when you need to clear account data without closing the account.
152///
153/// Fails with `AccountBorrowFailed` if any data borrow (shared or exclusive)
154/// is outstanding: the memset would mutate memory a live `Ref`/`RefMut`
155/// still points at.
156#[inline]
157pub fn zero_account_data(
158    account: &crate::account_view::AccountView<'_>,
159) -> Result<(), ProgramError> {
160    account.check_borrow_mut()?;
161    let len = account.data_len();
162    if len == 0 {
163        return Ok(());
164    }
165    // SAFETY: no data borrow is outstanding (checked above); the pointer and
166    // length describe this account's SVM-owned data region.
167    unsafe {
168        memset(account.data_ptr_unchecked(), 0, len);
169    }
170    Ok(())
171}