diff --git a/src/memory.rs b/src/memory.rs index 0854d2a..4b859d6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -352,6 +352,29 @@ impl MemoryInstance { Ok(()) } + /// Copy memory between two (possibly distinct) memory instances. + /// + /// If the same memory instance passed as `src` and `dst` then usual `copy` will be used. + pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> { + if Rc::ptr_eq(&src.0, &dst.0) { + // `transfer` is invoked with with same source and destination. Let's assume that regions may + // overlap and use `copy`. + return src.copy(src_offset, dst_offset, len); + } + + // Because memory references point to different memory instances, it is safe to `borrow_mut` + // both buffers at once (modulo `with_direct_access_mut`). + let mut src_buffer = src.buffer.borrow_mut(); + let mut dst_buffer = dst.buffer.borrow_mut(); + + let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range(); + let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range(); + + dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]); + + Ok(()) + } + /// Fill the memory region with the specified value. /// /// Semantically equivalent to `memset`. @@ -430,7 +453,8 @@ pub fn validate_memory(initial: Pages, maximum: Option) -> Result<(), Str #[cfg(test)] mod tests { - use super::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; + use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; + use std::rc::Rc; use Error; use memory_units::Pages; @@ -533,6 +557,47 @@ mod tests { } } + #[test] + fn transfer_works() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + + MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap(); + + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]); + } + + #[test] + fn transfer_still_works_with_same_memory() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + + MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap(); + + assert_eq!(src.get(0, 10).unwrap(), &[4, 5, 6, 3, 4, 5, 6, 7, 8, 9]); + } + + #[test] + fn transfer_oob_with_same_memory_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err()); + + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + } + + #[test] + fn transfer_oob_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + + assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err()); + + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); + } + #[test] fn clear() { let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);