Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

casting *mut u8 to &[u8] without std

I'm writing Rust code for WebAssembly to handle strings from JavaScript land.

Since WebAssembly has no real string type, I'm trying to pass a pointer to WebAssembly memory object which points to UTF-8 encoded string.

#[no_mangle]
pub extern "C" fn check(ptr: *mut u8, length: u32) -> u32 {
    unsafe {
        let buf: &[u8] = std::slice::from_raw_parts(ptr, length as usize);
        // do some operations on buf
        0
    }
}

It works fine, expect that I have to depend on the std crate, which bloats the final binary to about 600KB.

Is there any way to get rid of std::slice::from_raw_parts but still be able to cast a raw pointer to a slice?

like image 748
liuyanghejerry Avatar asked Jun 20 '18 06:06

liuyanghejerry


2 Answers

You cannot cast a raw pointer to a slice because in Rust, a slice is not a mere pointer, it is a pointer and a size (otherwise it could not be safe).

If you do not want to use std, you can use the core crate:

extern crate core;

#[no_mangle]
pub extern "C" fn check(ptr: *mut u8, length: u32) -> u32 {
    unsafe {
        let buf: &mut [u8] = core::slice::from_raw_parts_mut(ptr, length as usize);
    }
    // do some operations on buf
    0
}

The core crate is the part of the std crate suitable for embedded, i.e. without all the stuff that needs some allocation.

like image 174
Boiethios Avatar answered Oct 05 '22 15:10

Boiethios


It is possible to manually construct something similar to a slice, which is a fat pointer that consists of a thin pointer and a length. Then cast a pointer-to-this-construct to a pointer-to-slice.

This approach is not only unsafe, it also relies on Rust internals (memory layout of a slice) that are not guaranteed to remain stable between compiler version, or even systems I suppose. @Boiethios' answer is the way to go if you want to be sure that your code works correctly in the future. However, for educational purposes, the code below may still be interesting:

unsafe fn make_slice<'a>(ptr: *const u8, len: usize) -> &'a [u8] {
    // place pointer address and length in contiguous memory
    let x: [usize; 2] = [ptr as usize, len];

    // cast pointer to array as pointer to slice
    let slice_ptr = &x as * const _ as *const &[u8];

    // dereference pointer to slice, so we get a slice
    *slice_ptr
}

fn main() {
    let src: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
    let raw_ptr = &src[1] as *const u8;

    unsafe {
        println!("{:?}", make_slice(raw_ptr, 3));  // [2, 3, 4]
    }
}

(tested on playground with Rust Stable 1.26.2)

like image 37
MB-F Avatar answered Oct 05 '22 16:10

MB-F