 # Safe way to convert Vec<u8> of len n*4 to a Vec<Rgba> of len n

I have the following struct:

``````
#[repr(C)]
pub struct Rgba {
r: u8,
g: u8,
b: u8,
a: u8,
}

``````

I also have `v: Vec<u8>` of len n*4, guaranteed to be rgba format. Is there a way, without manually looping through every element, to "cast" this `Vec<u8> of len n*4` to a `Vec<rgba> of len n` ?

Given that:

``````alloc::Layout::<[u8; 4 * N]>::new() == alloc::Layout::<[Rgba; N]>::new()
``````

(because `mem::size_of::<Rgba>() == 4 * mem::size_of::<u8>()` AND because

`mem::align_of::<Rgba>() == mem::align_of::<u8>()`

(that last condition is paramount for the safety of such cast).

You can then define the following function:

``````#![feature(vec_into_raw_parts)]

fn transmute_vec (vec: Vec<u8>) -> Vec<Rgba>
{
use ::std::{alloc, mem};

const K: usize =
mem::size_of::<Rgba>() / mem::size_of::<u8>()
;

assert_eq!(
mem::align_of::<Rgba>(),
mem::align_of::<u8>(),
);

assert_eq!(vec.len() % K, 0);
assert_eq!(vec.capacity() % K, 0);
let (ptr, len, cap): (*mut u8, _, _) = vec.into_raw_parts();
unsafe {
// # Safety
//
//  - It is safe to transmute between `[u8; K]` and `Rgba`,
//
//      - (thanks to `Rgba` being a `#[repr(C)]` struct with `K` u8 fields)
//
//   - `Layout::<[u8; K * N]>::new() == Layout::<[Rgba; N]>::new()`
Vec::from_raw_parts(
ptr.cast::<Rgba>(),
len / K,
cap / K,
)
}
}
``````

The above requires `nightly` dues to the `vec_into_raw_parts` feature. You can reimplement it in stable with the following code:

``````trait VecIntoRawParts {
type Item;

fn into_raw_parts (self: Self)
-> (*mut Self::Item, usize, usize)
;
}

impl<T> VecIntoRawParts for Vec<T> {
type Item = T;

fn into_raw_parts (self: Self)
-> (*mut Self::Item, usize, usize)
{
let mut this = ::core::mem::ManuallyDrop::new(self);
let length = this.len();
let capacity = this.capacity();
(this.as_mut_ptr(), length, capacity)
}
}
``````
3 Likes

This can only be done safely if the capacity is a multiple of 4. Generally speaking that's not something you can guarantee unless you use `Vec::with_capacity` to create it.

https://doc.rust-lang.org/std/vec/struct.Vec.html

`Vec` does not guarantee any particular growth strategy when reallocating when full, nor when `reserve` is called. The current strategy is basic and it may prove desirable to use a non-constant growth factor. Whatever strategy is used will of course guarantee `O(1)` amortized `push` .

Because rust's current expansion strategy is to double the capacity on overfill, you will seldom run into this issue, but it is still possible:

``````fn main() {
let mut v = vec![1, 2, 3];
v.push(4);
println!("{}", v.capacity());  // Currently prints 6
}
``````

My crate `slice_of_array` will allow you to safely convert slices of the vec: This is to say, any owned data you pass around must be `Vec<u8>`, but you can easily view it as `&[Rgba]` or `&mut [Rgba]` at any time.

``````use slice_of_array::prelude::*;

unsafe impl slice_of_array::IsSliceomorphic for Rgba {
type Element = u8;
const LEN: usize = 4;
}

fn main() {
let v: Vec<u8> = vec![1, 2, 3, 255, 1, 2, 3, 255];

let rgba: &[Rgba] = v.nest(); // panics if len not divisible by 4
assert_eq!(rgba, Rgba { r: 1, g: 2, b: 3, a: 255 });
}
``````
6 Likes

This is interesting. CAPACITY (as opposed to length) being multiple of 4 is not something I considered.

1. I am reading data from a HTML Canvas Element: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData

2. The ImageData https://developer.mozilla.org/en-US/docs/Web/API/ImageData has a data field, which is an https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray

3. This is then read into Rust/wasm32/stdweb via https://docs.rs/stdweb/0.4.20/stdweb/web/struct.TypedArray.html

4. We get a vec via calling ".tovec" -- this definitely has length that is multiple of 4. As for capacity, I'm not sure.

In this case, capacity is equal to length, but only because the implementation of `stdweb::TypedArray::to_vec` uses `Vec::with_capacity`.

If you want to remain safe, you can do the following:

``````let slice: Box<[u8]> = vec.into();
let vec: Vec<u8> = slice.into();
``````

The docs of `Vec` guarantee that this will set `cap = len` and will only reallocate if necessary. (oddly enough, there is still no such guarantee given for `shrink_to_fit()`, so stay away from that for now)

1 Like