use crate::{ProgramError, ProgramResult};
#[inline(always)]
pub unsafe fn copy_nonoverlapping(dst: *mut u8, src: *const u8, len: usize) {
unsafe {
crate::syscalls::sol_memcpy_(dst, src, len as u64);
}
}
#[inline(always)]
pub unsafe fn copy(dst: *mut u8, src: *const u8, len: usize) {
unsafe {
crate::syscalls::sol_memmove_(dst, src, len as u64);
}
}
#[inline(always)]
pub unsafe fn fill(dst: *mut u8, byte: u8, len: usize) {
unsafe {
crate::syscalls::sol_memset_(dst, byte, len as u64);
}
}
#[inline(always)]
pub unsafe fn compare(left: *const u8, right: *const u8, len: usize) -> core::cmp::Ordering {
let mut result = 0i32;
unsafe {
crate::syscalls::sol_memcmp_(left, right, len as u64, &mut result as *mut i32);
}
match result {
0 => core::cmp::Ordering::Equal,
value if value < 0 => core::cmp::Ordering::Less,
_ => core::cmp::Ordering::Greater,
}
}
#[inline]
pub fn copy_bytes(dst: &mut [u8], src: &[u8]) -> ProgramResult {
if dst.len() < src.len() {
return Err(ProgramError::InvalidArgument);
}
if src.is_empty() {
return Ok(());
}
unsafe {
copy_nonoverlapping(dst.as_mut_ptr(), src.as_ptr(), src.len());
}
Ok(())
}
#[inline]
pub fn move_within(
buffer: &mut [u8],
src_start: usize,
len: usize,
dst_start: usize,
) -> ProgramResult {
let src_end = src_start
.checked_add(len)
.ok_or(ProgramError::InvalidArgument)?;
let dst_end = dst_start
.checked_add(len)
.ok_or(ProgramError::InvalidArgument)?;
if src_end > buffer.len() || dst_end > buffer.len() {
return Err(ProgramError::InvalidArgument);
}
if len == 0 || src_start == dst_start {
return Ok(());
}
unsafe {
copy(
buffer.as_mut_ptr().add(dst_start),
buffer.as_ptr().add(src_start),
len,
);
}
Ok(())
}
#[inline]
pub fn fill_bytes(buffer: &mut [u8], byte: u8) {
if buffer.is_empty() {
return;
}
unsafe {
fill(buffer.as_mut_ptr(), byte, buffer.len());
}
}
#[inline(always)]
pub fn zero_bytes(buffer: &mut [u8]) {
fill_bytes(buffer, 0);
}
#[inline]
pub fn compare_bytes(left: &[u8], right: &[u8]) -> core::cmp::Ordering {
let prefix_len = core::cmp::min(left.len(), right.len());
if prefix_len != 0 {
let prefix_order = unsafe { compare(left.as_ptr(), right.as_ptr(), prefix_len) };
if prefix_order != core::cmp::Ordering::Equal {
return prefix_order;
}
}
left.len().cmp(&right.len())
}
#[inline]
pub fn bytes_eq(left: &[u8], right: &[u8]) -> bool {
left.len() == right.len() && compare_bytes(left, right) == core::cmp::Ordering::Equal
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn copy_bytes_copies_prefix() {
let mut dst = [0u8; 5];
copy_bytes(&mut dst, &[1, 2, 3]).unwrap();
assert_eq!(dst, [1, 2, 3, 0, 0]);
}
#[test]
fn move_within_allows_overlap() {
let mut data = [1u8, 2, 3, 4, 5];
move_within(&mut data, 0, 4, 1).unwrap();
assert_eq!(data, [1, 1, 2, 3, 4]);
}
#[test]
fn fill_and_compare_bytes() {
let mut data = [9u8; 4];
zero_bytes(&mut data);
assert_eq!(data, [0u8; 4]);
assert!(bytes_eq(&data, &[0, 0, 0, 0]));
assert_eq!(compare_bytes(&[1, 2], &[1, 3]), core::cmp::Ordering::Less);
}
}