1use {super::*, crate::translate_mut};
2
3fn mem_op_consume(invoke_context: &mut InvokeContext, n: u64) -> Result<(), Error> {
4 let compute_cost = invoke_context.get_execution_cost();
5 let cost = compute_cost.mem_op_base_cost.max(
6 n.checked_div(compute_cost.cpi_bytes_per_unit)
7 .unwrap_or(u64::MAX),
8 );
9 invoke_context.compute_meter.consume_checked(cost)
10}
11
12pub(crate) fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
14where
15 N: Ord + num_traits::SaturatingSub,
16{
17 if src > dst {
20 src.saturating_sub(&dst) >= dst_len
21 } else {
22 dst.saturating_sub(&src) >= src_len
23 }
24}
25
26pub struct SyscallMemcpy {}
28impl BuiltinFunctionDefinition<InvokeContext<'_, '_>> for SyscallMemcpy {
29 type Error = Error;
30
31 fn rust(
32 invoke_context: &mut InvokeContext<'_, '_>,
33 dst_addr: u64,
34 src_addr: u64,
35 n: u64,
36 _arg4: u64,
37 _arg5: u64,
38 ) -> Result<u64, Error> {
39 mem_op_consume(invoke_context, n)?;
40
41 if !is_nonoverlapping(src_addr, n, dst_addr, n) {
42 return Err(SyscallError::CopyOverlapping.into());
43 }
44
45 memmove(invoke_context, dst_addr, src_addr, n)
47 }
48}
49
50pub struct SyscallMemmove {}
52impl BuiltinFunctionDefinition<InvokeContext<'_, '_>> for SyscallMemmove {
53 type Error = Error;
54 fn rust(
55 invoke_context: &mut InvokeContext<'_, '_>,
56 dst_addr: u64,
57 src_addr: u64,
58 n: u64,
59 _arg4: u64,
60 _arg5: u64,
61 ) -> Result<u64, Error> {
62 mem_op_consume(invoke_context, n)?;
63 memmove(invoke_context, dst_addr, src_addr, n)
64 }
65}
66
67pub struct SyscallMemcmp {}
69impl BuiltinFunctionDefinition<InvokeContext<'_, '_>> for SyscallMemcmp {
70 type Error = Error;
71 fn rust(
72 invoke_context: &mut InvokeContext<'_, '_>,
73 s1_addr: u64,
74 s2_addr: u64,
75 n: u64,
76 cmp_result_addr: u64,
77 _arg5: u64,
78 ) -> Result<u64, Error> {
79 mem_op_consume(invoke_context, n)?;
80 let check_aligned = invoke_context.get_check_aligned();
81 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
82
83 let s1 = translate_slice::<u8>(memory_mapping, s1_addr, n, check_aligned)?;
84 let s2 = translate_slice::<u8>(memory_mapping, s2_addr, n, check_aligned)?;
85
86 debug_assert_eq!(s1.len(), n as usize);
87 debug_assert_eq!(s2.len(), n as usize);
88 let result = unsafe { memcmp(s1, s2, n as usize) };
93
94 translate_mut!(
95 memory_mapping,
96 check_aligned,
97 let cmp_result_ref_mut: (&mut std::mem::MaybeUninit<i32>) = map(cmp_result_addr)?;
98 );
99 cmp_result_ref_mut.write(result);
100
101 Ok(0)
102 }
103}
104
105pub struct SyscallMemset {}
107impl BuiltinFunctionDefinition<InvokeContext<'_, '_>> for SyscallMemset {
108 type Error = Error;
109
110 fn rust(
111 invoke_context: &mut InvokeContext<'_, '_>,
112 dst_addr: u64,
113 c: u64,
114 n: u64,
115 _arg4: u64,
116 _arg5: u64,
117 ) -> Result<u64, Error> {
118 mem_op_consume(invoke_context, n)?;
119
120 let check_aligned = invoke_context.get_check_aligned();
121 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
122 translate_mut!(
123 memory_mapping,
124 check_aligned,
125 let s: (&mut [MaybeUninit<u8>]) = map(dst_addr, n)?;
126 );
127 s.fill(MaybeUninit::new(c as u8));
128 Ok(0)
129 }
130}
131
132fn memmove(
133 invoke_context: &mut InvokeContext,
134 dst_addr: u64,
135 src_addr: u64,
136 n: u64,
137) -> Result<u64, Error> {
138 let check_aligned = invoke_context.get_check_aligned();
139 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
140 touch_slice_mut::<MaybeUninit<u8>>(memory_mapping, dst_addr, n)?;
143 let slice = translate_slice_inner!(
144 memory_mapping,
145 AccessType::Store,
146 dst_addr,
147 n,
148 MaybeUninit<u8>,
149 check_aligned,
150 )?;
151 let src_ptr = translate_slice::<u8>(memory_mapping, src_addr, n, check_aligned)?.as_ptr();
152 unsafe { std::ptr::copy(src_ptr.cast(), slice as *mut MaybeUninit<u8>, n as usize) };
153 Ok(0)
154}
155
156unsafe fn memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
158 let (s1pre, s1mid, s1end) = unsafe {
159 s1.get_unchecked(..n).align_to::<u128>()
161 };
162 let mut s2ptr = s2.as_ptr();
163 for s1pre_byte in s1pre.iter().copied() {
164 unsafe {
165 let s2pre_byte = *s2ptr;
168 if s1pre_byte != s2pre_byte {
169 return i32::from(s1pre_byte).wrapping_sub(s2pre_byte.into());
170 }
171 s2ptr = s2ptr.add(1);
172 }
173 }
174 for s1mid_value in s1mid.iter().copied() {
175 let s2mid_value = unsafe {
176 s2ptr.cast::<u128>().read_unaligned().to_le()
180 };
181 if s1mid_value != s2mid_value {
182 let (s1_word, s2_word) = if s1mid_value as u64 != s2mid_value as u64 {
187 let w1 = s1mid_value as u64;
188 let w2 = s2mid_value as u64;
189 (w1, w2)
190 } else {
191 let w1 = (s1mid_value >> 64) as u64;
192 let w2 = (s2mid_value >> 64) as u64;
193 (w1, w2)
194 };
195 let shift = (s1_word ^ s2_word).trailing_zeros() & !7;
196 let b1 = (s1_word >> shift) as u8;
197 let b2 = (s2_word >> shift) as u8;
198 return i32::from(b1).wrapping_sub(b2.into());
199 }
200 unsafe {
201 s2ptr = s2ptr.add(std::mem::size_of::<u128>());
204 }
205 }
206 for s1end_byte in s1end.iter().copied() {
207 unsafe {
208 let s2end_byte = *s2ptr;
210 if s1end_byte != s2end_byte {
211 return i32::from(s1end_byte).wrapping_sub(s2end_byte.into());
212 }
213 s2ptr = s2ptr.add(1);
214 }
215 }
216 0
217}
218
219#[cfg(test)]
220#[allow(clippy::indexing_slicing)]
221#[allow(clippy::arithmetic_side_effects)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_is_nonoverlapping() {
227 for dst in 0..8 {
228 assert!(is_nonoverlapping(10, 3, dst, 3));
229 }
230 for dst in 8..13 {
231 assert!(!is_nonoverlapping(10, 3, dst, 3));
232 }
233 for dst in 13..20 {
234 assert!(is_nonoverlapping(10, 3, dst, 3));
235 }
236 assert!(is_nonoverlapping::<u8>(255, 3, 254, 1));
237 assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
238 }
239}