use {super::*, crate::translate_mut};
fn mem_op_consume(invoke_context: &mut InvokeContext, n: u64) -> Result<(), Error> {
let compute_cost = invoke_context.get_execution_cost();
let cost = compute_cost.mem_op_base_cost.max(
n.checked_div(compute_cost.cpi_bytes_per_unit)
.unwrap_or(u64::MAX),
);
consume_compute_meter(invoke_context, cost)
}
pub(crate) fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
where
N: Ord + num_traits::SaturatingSub,
{
if src > dst {
src.saturating_sub(&dst) >= dst_len
} else {
dst.saturating_sub(&src) >= src_len
}
}
declare_builtin_function!(
SyscallMemcpy,
fn rust(
invoke_context: &mut InvokeContext,
dst_addr: u64,
src_addr: u64,
n: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
mem_op_consume(invoke_context, n)?;
if !is_nonoverlapping(src_addr, n, dst_addr, n) {
return Err(SyscallError::CopyOverlapping.into());
}
memmove(invoke_context, dst_addr, src_addr, n, memory_mapping)
}
);
declare_builtin_function!(
SyscallMemmove,
fn rust(
invoke_context: &mut InvokeContext,
dst_addr: u64,
src_addr: u64,
n: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
mem_op_consume(invoke_context, n)?;
memmove(invoke_context, dst_addr, src_addr, n, memory_mapping)
}
);
declare_builtin_function!(
SyscallMemcmp,
fn rust(
invoke_context: &mut InvokeContext,
s1_addr: u64,
s2_addr: u64,
n: u64,
cmp_result_addr: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
mem_op_consume(invoke_context, n)?;
let s1 = translate_slice::<u8>(
memory_mapping,
s1_addr,
n,
invoke_context.get_check_aligned(),
)?;
let s2 = translate_slice::<u8>(
memory_mapping,
s2_addr,
n,
invoke_context.get_check_aligned(),
)?;
debug_assert_eq!(s1.len(), n as usize);
debug_assert_eq!(s2.len(), n as usize);
let result = unsafe { memcmp(s1, s2, n as usize) };
translate_mut!(
memory_mapping,
invoke_context.get_check_aligned(),
let cmp_result_ref_mut: &mut i32 = map(cmp_result_addr)?;
);
*cmp_result_ref_mut = result;
Ok(0)
}
);
declare_builtin_function!(
SyscallMemset,
fn rust(
invoke_context: &mut InvokeContext,
dst_addr: u64,
c: u64,
n: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
mem_op_consume(invoke_context, n)?;
translate_mut!(
memory_mapping,
invoke_context.get_check_aligned(),
let s: &mut [u8] = map(dst_addr, n)?;
);
s.fill(c as u8);
Ok(0)
}
);
fn memmove(
invoke_context: &mut InvokeContext,
dst_addr: u64,
src_addr: u64,
n: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
translate_mut!(
memory_mapping,
invoke_context.get_check_aligned(),
let dst_ref_mut: &mut [u8] = map(dst_addr, n)?;
);
let dst_ptr = dst_ref_mut.as_mut_ptr();
let src_ptr = translate_slice::<u8>(
memory_mapping,
src_addr,
n,
invoke_context.get_check_aligned(),
)?
.as_ptr();
unsafe { std::ptr::copy(src_ptr, dst_ptr, n as usize) };
Ok(0)
}
unsafe fn memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
for i in 0..n {
let a = *s1.get_unchecked(i);
let b = *s2.get_unchecked(i);
if a != b {
return (a as i32).saturating_sub(b as i32);
};
}
0
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::arithmetic_side_effects)]
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::<u8>(255, 3, 254, 1));
assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
}
}