Unsafe memcpy w/o &mut reference

Is there an unsafe memcpy w/o relying on a &mut reference? For instance I want to copy b2, b3, b4 into b at specified locations w/o relying on a &mut reference to b. Is this even possible?

let b : [u8; 100] = [0; 100]; 
let b2 : [u8; 10] = [1; 10];
let b3 : [u8; 10] = [2; 10];
let b4 : [u8; 10] = [3; 10];

If you want to avoid creating a &mut reference to b, then you can use addr_of_mut!() to get a raw pointer to b, <*mut _>::add() to offset the pointer, then <*mut _>::copy_from_nonoverlapping() to copy the values. For instance (Rust Playground):

use std::ptr::addr_of_mut;

let mut b: [u8; 100] = [0; 100];
let b2: [u8; 10] = [1; 10];
let b3: [u8; 10] = [2; 10];
let b4: [u8; 10] = [3; 10];
unsafe {
    let b_ptr: *mut u8 = addr_of_mut!(b).cast::<u8>();
    b_ptr.add(5).copy_from_nonoverlapping(b2.as_ptr(), 10);
    b_ptr.add(20).copy_from_nonoverlapping(b3.as_ptr(), 10);
    b_ptr.add(35).copy_from_nonoverlapping(b4.as_ptr(), 10);
}
println!("{b:?}");

However, writing to a raw pointer in this way still requires mutable access, so it is equivalent to first creating a &mut reference to a subslice of b then writing to that. So it might not solve your problem here. Why exactly are you trying not to rely on a &mut reference?

Thanks. Here is the larger context - I have a large buffer and I have multiple threads writing into it. Now I can get an offset into the larger buffer using atomics pretty easily. However when I copy into the buffer I don't want to rely on having &mut reference to it, because in order to do so I have to serialize the writes. I would like the writes to happen in parallel.

The direct translation of memcpy is copy_nonoverlapping in std::ptr - Rust, which you'll notice takes pointers, not references.

But why do you even have this problem? Especially for u8s, where you can use copy_from_slice?

How have you shared the buffer, and how do threads coordinate on what slices they can write to so they don't overlap? (Note that a data race is UB no matter how you achieve it.)

1 Like

You will need to write your own synchronization with UnsafeCell or you use [u8]::split_at_mut at some point to split the buffer in multiple independently writable parts.

3 Likes

I second this. Can you set up your parallel workers/threads/tasks to already take the target subslice, when they start to work on a subtask? This worked for me, using

No you don't if you perform borrow splitting. You can also rely on a parallelism crate such as Rayon that abstracts away these mundane details of writing parallel code.

1 Like