#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
use core::mem::MaybeUninit;
#[cfg(any(target_os = "solana", target_arch = "bpf"))]
pub mod syscalls {
pub use solana_define_syscall::definitions::{
sol_memcmp_, sol_memcpy_, sol_memmove_, sol_memset_,
};
}
#[cfg(any(test, not(any(target_os = "solana", target_arch = "bpf"))))]
fn is_nonoverlapping(src: usize, src_len: usize, dst: usize, dst_len: usize) -> bool {
if src > dst {
src.saturating_sub(dst) >= dst_len
} else {
dst.saturating_sub(src) >= src_len
}
}
#[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
#[allow(clippy::arithmetic_side_effects)]
pub mod stubs {
use super::is_nonoverlapping;
pub unsafe fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
assert!(
is_nonoverlapping(src as usize, n, dst as usize, n),
"memcpy does not support overlapping regions"
);
core::ptr::copy_nonoverlapping(src, dst, n);
}
pub unsafe fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
core::ptr::copy(src, dst, n);
}
pub unsafe fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
let mut i = 0;
while i < n {
let a = *s1.add(i);
let b = *s2.add(i);
if a != b {
*result = a as i32 - b as i32;
return;
}
i += 1;
}
*result = 0
}
pub unsafe fn sol_memset(s: *mut u8, c: u8, n: usize) {
let s = core::slice::from_raw_parts_mut(s, n);
for val in s.iter_mut().take(n) {
*val = c;
}
}
}
#[inline]
pub unsafe fn sol_memcpy(dst: &mut [u8], src: &[u8], n: usize) {
#[cfg(any(target_os = "solana", target_arch = "bpf"))]
syscalls::sol_memcpy_(dst.as_mut_ptr(), src.as_ptr(), n as u64);
#[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
stubs::sol_memcpy(dst.as_mut_ptr(), src.as_ptr(), n);
}
#[inline]
pub unsafe fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
#[cfg(any(target_os = "solana", target_arch = "bpf"))]
syscalls::sol_memmove_(dst, src, n as u64);
#[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
stubs::sol_memmove(dst, src, n);
}
#[inline]
pub unsafe fn sol_memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
let mut result: MaybeUninit<i32> = MaybeUninit::uninit();
#[cfg(any(target_os = "solana", target_arch = "bpf"))]
syscalls::sol_memcmp_(s1.as_ptr(), s2.as_ptr(), n as u64, result.as_mut_ptr());
#[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
stubs::sol_memcmp(s1.as_ptr(), s2.as_ptr(), n, result.as_mut_ptr());
result.assume_init()
}
#[inline]
pub unsafe fn sol_memset(s: &mut [u8], c: u8, n: usize) {
#[cfg(any(target_os = "solana", target_arch = "bpf"))]
syscalls::sol_memset_(s.as_mut_ptr(), c, n as u64);
#[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
stubs::sol_memset(s.as_mut_ptr(), c, n);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_nonoverlapping() {
for dst in 0..8 {
assert!(is_nonoverlapping(10, 3, dst, 3));
}
for dst in 8..13 {
assert!(!is_nonoverlapping(10, 3, dst, 3));
}
for dst in 13..20 {
assert!(is_nonoverlapping(10, 3, dst, 3));
}
assert!(is_nonoverlapping(usize::MAX, 3, usize::MAX - 1, 1));
assert!(!is_nonoverlapping(usize::MAX, 2, usize::MAX - 1, 3));
}
}