solana_program_memory/
lib.rs

1#![no_std]
2
3//! Basic low-level memory operations.
4//!
5//! Within the SBF environment, these are implemented as syscalls and executed by
6//! the runtime in native code.
7
8use core::mem::MaybeUninit;
9
10#[cfg(target_os = "solana")]
11pub mod syscalls {
12    pub use solana_define_syscall::definitions::{
13        sol_memcmp_, sol_memcpy_, sol_memmove_, sol_memset_,
14    };
15}
16
17/// Check that two regions do not overlap.
18#[cfg(any(test, not(target_os = "solana")))]
19fn is_nonoverlapping(src: usize, src_len: usize, dst: usize, dst_len: usize) -> bool {
20    // If the absolute distance between the ptrs is at least as big as the size of the other,
21    // they do not overlap.
22    if src > dst {
23        src.saturating_sub(dst) >= dst_len
24    } else {
25        dst.saturating_sub(src) >= src_len
26    }
27}
28
29#[cfg(not(target_os = "solana"))]
30#[allow(clippy::arithmetic_side_effects)]
31pub mod stubs {
32    use super::is_nonoverlapping;
33    /// # Safety
34    pub unsafe fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
35        // cannot be overlapping
36        assert!(
37            is_nonoverlapping(src as usize, n, dst as usize, n),
38            "memcpy does not support overlapping regions"
39        );
40        core::ptr::copy_nonoverlapping(src, dst, n);
41    }
42    /// # Safety
43    pub unsafe fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
44        core::ptr::copy(src, dst, n);
45    }
46    /// # Safety
47    pub unsafe fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
48        let mut i = 0;
49        while i < n {
50            let a = *s1.add(i);
51            let b = *s2.add(i);
52            if a != b {
53                *result = a as i32 - b as i32;
54                return;
55            }
56            i += 1;
57        }
58        *result = 0
59    }
60    /// # Safety
61    pub unsafe fn sol_memset(s: *mut u8, c: u8, n: usize) {
62        let s = core::slice::from_raw_parts_mut(s, n);
63        for val in s.iter_mut().take(n) {
64            *val = c;
65        }
66    }
67}
68
69/// Like C `memcpy`.
70///
71/// # Arguments
72///
73/// - `dst` - Destination
74/// - `src` - Source
75/// - `n` - Number of bytes to copy
76///
77/// # Errors
78///
79/// When executed within a SBF program, the memory regions spanning `n` bytes
80/// from from the start of `dst` and `src` must be mapped program memory. If not,
81/// the program will abort.
82///
83/// The memory regions spanning `n` bytes from `dst` and `src` from the start
84/// of `dst` and `src` must not overlap. If they do, then the program will abort
85/// or, if run outside of the SBF VM, will panic.
86///
87/// # Safety
88///
89/// This function does not verify that `n` is less than or equal to the
90/// lengths of the `dst` and `src` slices passed to it &mdash; it will copy
91/// bytes to and from beyond the slices.
92///
93/// Specifying an `n` greater than either the length of `dst` or `src` will
94/// likely introduce undefined behavior.
95#[inline]
96pub unsafe fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
97    #[cfg(target_os = "solana")]
98    syscalls::sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
99
100    #[cfg(not(target_os = "solana"))]
101    stubs::sol_memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
102}
103
104/// Like C `memmove`.
105///
106/// # Arguments
107///
108/// - `dst` - Destination
109/// - `src` - Source
110/// - `n` - Number of bytes to copy
111///
112/// # Errors
113///
114/// When executed within a SBF program, the memory regions spanning `n` bytes
115/// from from `dst` and `src` must be mapped program memory. If not, the program
116/// will abort.
117///
118/// # Safety
119///
120/// The same safety rules apply as in [`ptr::copy`].
121///
122/// [`ptr::copy`]: https://doc.rust-lang.org/core/ptr/fn.copy.html
123#[inline]
124pub unsafe fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
125    #[cfg(target_os = "solana")]
126    syscalls::sol_memmove_(dst, src, n as u64);
127
128    #[cfg(not(target_os = "solana"))]
129    stubs::sol_memmove(dst, src, n);
130}
131
132/// Like C `memcmp`.
133///
134/// # Arguments
135///
136/// - `s1` - Slice to be compared
137/// - `s2` - Slice to be compared
138/// - `n` - Number of bytes to compare
139///
140/// # Errors
141///
142/// When executed within a SBF program, the memory regions spanning `n` bytes
143/// from from the start of `dst` and `src` must be mapped program memory. If not,
144/// the program will abort.
145///
146/// # Safety
147///
148/// It does not verify that `n` is less than or equal to the lengths of the
149/// `dst` and `src` slices passed to it &mdash; it will read bytes beyond the
150/// slices.
151///
152/// Specifying an `n` greater than either the length of `dst` or `src` will
153/// likely introduce undefined behavior.
154#[inline]
155pub unsafe fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
156    let mut result: MaybeUninit<i32> = MaybeUninit::uninit();
157
158    #[cfg(target_os = "solana")]
159    syscalls::sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, result.as_mut_ptr());
160
161    #[cfg(not(target_os = "solana"))]
162    stubs::sol_memcmp(s1.as_ptr(), s2.as_ptr(), n, result.as_mut_ptr());
163
164    result.assume_init()
165}
166
167/// Like C `memset`.
168///
169/// # Arguments
170///
171/// - `s` - Slice to be set
172/// - `c` - Repeated byte to set
173/// - `n` - Number of bytes to set
174///
175/// # Errors
176///
177/// When executed within a SBF program, the memory region spanning `n` bytes
178/// from from the start of `s` must be mapped program memory. If not, the program
179/// will abort.
180///
181/// # Safety
182///
183/// This function does not verify that `n` is less than or equal to the length
184/// of the `s` slice passed to it &mdash; it will write bytes beyond the
185/// slice.
186///
187/// Specifying an `n` greater than the length of `s` will likely introduce
188/// undefined behavior.
189#[inline]
190pub unsafe fn sol_memset(s: &mut [u8], c: u8, n: usize) {
191    #[cfg(target_os = "solana")]
192    syscalls::sol_memset_(s.as_mut_ptr(), c, n as u64);
193
194    #[cfg(not(target_os = "solana"))]
195    stubs::sol_memset(s.as_mut_ptr(), c, n);
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_is_nonoverlapping() {
204        for dst in 0..8 {
205            assert!(is_nonoverlapping(10, 3, dst, 3));
206        }
207        for dst in 8..13 {
208            assert!(!is_nonoverlapping(10, 3, dst, 3));
209        }
210        for dst in 13..20 {
211            assert!(is_nonoverlapping(10, 3, dst, 3));
212        }
213        assert!(is_nonoverlapping(usize::MAX, 3, usize::MAX - 1, 1));
214        assert!(!is_nonoverlapping(usize::MAX, 2, usize::MAX - 1, 3));
215    }
216}