Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to slice a large Vec<i32> as &[u8]?

Tags:

rust

I don't know how to convert a Vec<i32> into a &[u8] slice.

fn main() {
    let v: Vec<i32> = vec![1; 100_000_000];
    let v_bytes: &[u8] = /* ... */;
}

I want to write a large Vec<i32> to a file so I can read it back at a future time.

like image 351
jimjampez Avatar asked Mar 13 '15 16:03

jimjampez


2 Answers

You can use std::slice::from_raw_parts:

let v_bytes: &[u8] = unsafe {
    std::slice::from_raw_parts(
        v.as_ptr() as *const u8,
        v.len() * std::mem::size_of::<i32>(),
    )
};

Following the comments on this answer, you should wrap this code in a function and have the return value borrow the input, so that you use the borrow checker as far as possible:

fn as_u8_slice(v: &[i32]) -> &[u8] {
    unsafe {
        std::slice::from_raw_parts(
            v.as_ptr() as *const u8,
            v.len() * std::mem::size_of::<i32>(),
        )
    }
}
like image 143
swizard Avatar answered Nov 09 '22 23:11

swizard


Since Rust 1.30, the best solution is to use slice::align_to:

fn main() {
    let v: Vec<i32> = vec![1; 8];

    let (head, body, tail) = unsafe { v.align_to::<u8>() };
    assert!(head.is_empty());
    assert!(tail.is_empty());

    println!("{:#x?}", body);
}

This properly handles the cases where the alignment of the first type and the second type do not match. In this example, I ensure that the alignment of the i32 is greater than that of the u8 via the assert! statements.


I took @swizards answer and ran with it a bit to get the other side of the coin - reading the vector back in:

use std::fs::File;
use std::io::{Read, Write};
use std::{mem, slice};

fn as_u8_slice(v: &[i32]) -> &[u8] {
    let element_size = mem::size_of::<i32>();
    unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * element_size) }
}

fn from_u8(v: Vec<u8>) -> Vec<i32> {
    let data = v.as_ptr();
    let len = v.len();
    let capacity = v.capacity();
    let element_size = mem::size_of::<i32>();

    // Make sure we have a proper amount of capacity (may be overkill)
    assert_eq!(capacity % element_size, 0);
    // Make sure we are going to read a full chunk of stuff
    assert_eq!(len % element_size, 0);

    unsafe {
        // Don't allow the current vector to be dropped
        // (which would invalidate the memory)
        mem::forget(v);

        Vec::from_raw_parts(
            data as *mut i32,
            len / element_size,
            capacity / element_size,
        )
    }
}

fn do_write(filename: &str, v: &[i32]) {
    let mut f = File::create(filename).unwrap();
    f.write_all(as_u8_slice(v)).unwrap();
}

fn do_read(filename: &str) -> Vec<i32> {
    let mut f = File::open(filename).unwrap();
    let mut bytes = Vec::new();

    f.read_to_end(&mut bytes).unwrap();

    from_u8(bytes)
}

fn main() {
    let v = vec![42; 10];
    do_write("vector.dump", &v);
    let v2 = do_read("vector.dump");

    assert_eq!(v, v2);
    println!("{:?}", v2)
}
like image 40
Shepmaster Avatar answered Nov 09 '22 23:11

Shepmaster