Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a function to align a slice to the right alignment #49

Closed
Kerollmops opened this issue Jan 10, 2021 · 3 comments · Fixed by #50
Closed

Introduce a function to align a slice to the right alignment #49

Kerollmops opened this issue Jan 10, 2021 · 3 comments · Fixed by #50

Comments

@Kerollmops
Copy link

Kerollmops commented Jan 10, 2021

Hello @Lokathor,

I wanted to switch from zerocopy to bytemuck for multiple reasons, and one of the missing features for both crates is the ability to realign a slice of type A into a Vec of type B, which is allowed to allocate. What do you think about introducing this to this crate (under the allocation feature) and introducing that for Box?

This function would be preferable not to restrict the size (where the length of the input must be the same as the output Vec). It would make this function useless for when you want to transpose differently sized types (e.g. u8 into u16) like for the try_cast_vec function.

Another solution would be to make the try_cast_vec function change the length and capacity of the Vec. This seems a lot better but does seem to be a breaking change in the sense that more results could return an Ok(Vec) instead of an error and that the Vec is no more of the same length than the input one.

// An idea of the possible function signature
fn try_cast_slice_realign_vec<A: Pod, B: Pod>(input: &[A]) -> Result<Vec<B>, PodCastError>;

// Another even better function signature which will allocate only if alignment is invalid
// Note that Cow is in the alloc Rust library
fn try_cast_slice_realign_cow<A: Pod, B: Pod>(input: &[A]) -> Result<Cow<[B]>, PodCastError>;

Could maybe be implemented this way:

let bytes = bytes_of(input);
let len = input.len();
let elem_size = mem::size_of::<B>();

// ensure that it is the alignment that is wrong
// and the length is valid
if len % elem_size == 0 && align_of::<A>() != align_of::<B>() {
    let elems = len / elem_size;
    let mut vec = Vec::<B>::with_capacity(elems);

    unsafe {
        let dst = vec.as_mut_ptr() as *mut u8;
        ptr::copy_nonoverlapping(bytes.as_ptr(), dst, len);
        vec.set_len(elems);
    }

    Ok(Cow::Owned(vec))
}
@Lokathor
Copy link
Owner

The capacity and alignment of the vec's allocation can't change during casting, it causes UB in the allocator.

I'm not sure I'm fully clear on the requirements here. Can't you just cast the slice, then gather the slice into a new vec once you've cast it?

let new_slice: &[u16] = cast_slice(old_slice);
let new_vec: Vec<_> = new_slice.iter().copied().collect();

@Kerollmops
Copy link
Author

Kerollmops commented Jan 12, 2021

What I would like to be able to do is to cast a slice of bytes into an aligned Vec of T even if the bytes are not correctly aligned to be freely cast. The easiest way to do that, in my mind, is to create an empty Vec of T with the right alignment for T and enough capacity, and then copy the bytes into the Vec directly. What do you think?

@Lokathor
Copy link
Owner

Ah, interesting. I follow now.

I'm sure we could make something like that.

The term "cast" doesn't quite fit, that's why i was confused, since you're doing a new allocation and a copy and all that, but you could turn a alice of pod into a vec of some other pod type and ignore the alignment of the source slice in the process.

This seems like a small enough request to handle it the next time i sit down to do Rust. Might have time tonight even.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants