Mutating where a pointer points

Suppose I have some closure, A, with a mutable reference to some byte slice &mut [u8]. In closure A, to obtain a pointer to the byte slice, I just coerce the mutable reference into a mutable pointer.

When reading data from UDP sockets, we must supply a mutable byte slice to be filled. Suppose the slice I supply has k length (e.g., 10000). When a packet gets written to the byte slice, it may only fill up j < k bytes (e.g., 1400). If I want to pass this data into another closure, B, I have to heap allocate the bytes and pass a pointer to closure B (e.g., Bytes or BytesMut does this implicitly when i clone_from_slice). However, I would like to save a clone. So, i have an idea that I'd like others to help me with.

Instead of supplying an &mut [u8] directly, what if we pre-allocated a BytesMut structure, and then passed a &mut [u8] (which is a reference to some heap-allocated subset of the slice in the BytesMut) to the udp socket. Then, after we read j bytes, we advance the cursor, take() the new bytes (to create a Bytes object), then pass the Bytes outside of closure A. The next step would be mutating "where" the &mut [u8] points to. Could we do this by saving a *mut &mut [u8], and dereferencing that and updating "where" the &mut [u8] points to? Can we change "where" a reference points to, even though it was already passed into a closure outside of A? Also, suppose there is a single event loop, so we don't have to worry much about synchronization.

The purpose, in doing this, would be to reduce a single clone.

You should also consider the split_at_mut method.

1 Like

To illustrate @alice's suggestion:

let buffer: &/*'buffer*/mut [u8] = &mut [0_u8; 10_000];
let mut read_into_buffer = {
    let mut buffer = &mut *buffer;
    move |bytes_read: &'_ /* 'input */ [u8]| -> &'_ /* 'buffer */ [u8] {
        assert!(bytes_read.len() <= buffer.len());
        let buffer_: &mut [u8] = ::core::mem::replace(&mut buffer, &mut []);
        let (head, tail) = buffer_.split_at_mut(bytes_read.len());
        buffer = tail;
        head.copy_from_slice(bytes_read);
        head
    }
};
let first_read = {
    let local = [42, 27];
    read_into_buffer(&local)
};
dbg!(first_read); // 42, 27
let snd_read = {
    let local = [3, 14];
    read_into_buffer(&local)
};
dbg!(snd_read);  // 3, 14
dbg!(read_into_buffer(&[28])); // 28
dbg!(&buffer[.. 12]); // 42, 27, 3, 14, 28

Explanation

To understand why the need to replace there, see the following extension method on &'short mut &'long mut [u8]:

trait TakePrefix<'long> : 'long {
    fn take_prefix<'short> (
        self: &'short mut Self,
        prefix_len: usize,
    ) -> Option<&'long mut [u8]>
    ;
}
impl<'long> TakePrefix<'long>
    for &'long mut [u8]
{
    fn take_prefix<'short> (
        self: &'short mut &'long mut [u8],
        prefix_len: usize,
    ) -> Option<&'long mut [u8]>
    {Some({
        if prefix_len > self.len() {
            return None;
        }
        // a reborrow only yields `&'short mut [u8]`,
        // but we need `&'long mut [u8]`.
        // Here, `mem::replace` and `split_at_mut` come to the rescue!
        // this = *self; *self = &mut []
        let this: &'long mut [u8] = ::core::mem::replace(
            &mut    *self,
                    &mut [],
        );
        let (head, this) = this.split_at_mut(prefix_len);
        *self = this;
        head
    })}
}


fn main ()
{
    let buffer: &mut [u8] = &mut [0_u8; 10_000];
    let mut read_into_buffer = {
        let mut buffer = &mut *buffer;
        move |bytes_read: &'_ [u8]| -> &'_ [u8] {
            let head = buffer.take_prefix(bytes_read.len()).unwrap();
            head.copy_from_slice(bytes_read);
            head
        }
    };
    // ...
}
3 Likes

Beautiful. Thanks Alice and Yandros.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.